--- /dev/null
+Package: s2
+Title: Spherical Geometry Operators Using the S2 Geometry Library
+Version: 1.0.4
+Authors@R: c(
+ person(given = "Dewey",
+ family = "Dunnington",
+ role = c("aut"),
+ email = "dewey@fishandwhistle.net",
+ comment = c(ORCID = "0000-0002-9415-4582")),
+ person(given = "Edzer",
+ family = "Pebesma",
+ role = c("aut", "cre"),
+ email = "edzer.pebesma@uni-muenster.de",
+ comment = c(ORCID = "0000-0001-8049-7069")),
+ person("Ege", "Rubak", email="rubak@math.aau.dk", role = c("aut")),
+ person("Jeroen", "Ooms", , "jeroen.ooms@stat.ucla.edu", role = "ctb", comment = "configure script"),
+ person(family = "Google, Inc.", role = "cph", comment = "Original s2geometry.io source code")
+ )
+Description: Provides R bindings for Google's s2 library for geometric calculations on
+ the sphere. High-performance constructors and exporters provide high compatibility
+ with existing spatial packages, transformers construct new geometries from existing
+ geometries, predicates provide a means to select geometries based on spatial
+ relationships, and accessors extract information about geometries.
+License: Apache License (== 2.0)
+Encoding: UTF-8
+LazyData: true
+RoxygenNote: 7.1.1
+LinkingTo: Rcpp, wk
+Imports: Rcpp, wk
+Suggests: testthat, vctrs
+URL: https://r-spatial.github.io/s2/, https://github.com/r-spatial/s2,
+ https://s2geometry.io/
+BugReports: https://github.com/r-spatial/s2/issues
+Depends: R (>= 3.0.0)
+NeedsCompilation: yes
+Packaged: 2021-01-04 15:35:03 UTC; edzer
+Author: Dewey Dunnington [aut] (<https://orcid.org/0000-0002-9415-4582>),
+ Edzer Pebesma [aut, cre] (<https://orcid.org/0000-0001-8049-7069>),
+ Ege Rubak [aut],
+ Jeroen Ooms [ctb] (configure script),
+ Google, Inc. [cph] (Original s2geometry.io source code)
+Maintainer: Edzer Pebesma <edzer.pebesma@uni-muenster.de>
+Repository: CRAN
+Date/Publication: 2021-01-05 05:10:02 UTC
--- /dev/null
+b0d140c3a6a007b1192cae3e0569df2f *DESCRIPTION
+5129788c48d634be1089286a041d824a *NAMESPACE
+458d1fd3761859921eb8b30d0a27fd3b *NEWS.md
+49c283041cce525dd99897186dcc8584 *R/RcppExports.R
+8567a6b7a3c75cad01a581303546fced *R/data.R
+8aa358cca3ffcc922b9bde02ef1accd4 *R/s2-accessors.R
+4ba8ec7d16efeadbc21c3ab5ac3dc6a6 *R/s2-bounds.R
+a9b85fd1af5d8ffba7f46f9d16b333ee *R/s2-constructors-formatters.R
+0968515e318a2c7a7372bddad1be6c23 *R/s2-earth.R
+f29b8dcf891e34b8feae519619914151 *R/s2-geography.R
+f7370a030a2faf0fb9a79166fc012ea2 *R/s2-lnglat.R
+11b87d96773046950186957f2af9478b *R/s2-matrix.R
+e10121cec72be69c2500e9121fcc128e *R/s2-options.R
+1bab90d590a0e66114c86739ba0d6ce4 *R/s2-package.R
+e78c2f5f3bc790e3fa91d3f5e037754e *R/s2-point.R
+447d319c0cbec576de0695a518292089 *R/s2-predicates.R
+382b5047b50a837dee2795239ec216ad *R/s2-transformers.R
+28d0bdc778944f01d0203a6083305b1a *R/s2-xptr.R
+4e8bc5586b795db8a2f8e65074ad05c1 *R/utils.R
+514ae694f7596f697979c5cbce1fbc37 *R/vctrs.R
+474285eb7c0d146036962c8f7248d1bb *R/zzz.R
+27f780fb97d6a58b2b6453c28ab740df *README.md
+a1c350c18af8b26ad476e0bee45ce1d3 *build/partial.rdb
+cc6eebf14a3f516ec74168f3ea416fb2 *cleanup
+9401515addc6f00f403443fd0715fcf9 *configure
+d41d8cd98f00b204e9800998ecf8427e *configure.win
+6d0a8453b0e12b4654d468d1e7248961 *data/s2_data_tbl_cities.rda
+b0b89f3499ca649d9a78f77096e4f5da *data/s2_data_tbl_countries.rda
+5bbeed937d4c3e766c02ce396f77cefc *data/s2_data_tbl_timezones.rda
+afabc65ce7a01abdb14efb1abccd7247 *inst/include/cpp-compat.h
+8428235fc1165d0c32375e2e21a686fe *inst/include/s2/_fp_contract_off.h
+782cb0deddf3125910ebf4b02a8efc46 *inst/include/s2/base/casts.h
+4c10c37f35c3ea32da84e18e427e0a8f *inst/include/s2/base/commandlineflags.h
+95d68a4bda8ac1ed1238c06999c7fe8f *inst/include/s2/base/integral_types.h
+ab21c7db7743b7fbef680e8edd02c198 *inst/include/s2/base/log_severity.h
+23e37fa14dfbbf7e1e232816f351d7cd *inst/include/s2/base/logging.h
+84003e3ea7ed2316b94891afd7d425f0 *inst/include/s2/base/mutex.h
+2dca5b2433d232fc5fd572920892de77 *inst/include/s2/base/port.h
+7217ae2988c45fe3c657a6fbb5a01334 *inst/include/s2/base/spinlock.h
+c71fa2b58596fe3acc23b3191fb30c01 *inst/include/s2/base/stringprintf.h
+a5325b3b1a8569a91a072858d9961a76 *inst/include/s2/base/strtoint.h
+8a23b88128feaefecac82381870b7e1b *inst/include/s2/base/timer.h
+5c886186eca3428ebe67594a29a43066 *inst/include/s2/encoded_s2cell_id_vector.h
+f20c096d5c4d4272d6160b94321986e8 *inst/include/s2/encoded_s2point_vector.h
+8c71134bbac741ab14ad58645c587efd *inst/include/s2/encoded_s2shape_index.h
+90dca9b7842ad1910d1de858afad423c *inst/include/s2/encoded_string_vector.h
+40708275f82de6dd1d212c57c36ecee8 *inst/include/s2/encoded_uint_vector.h
+e7cbe5a6c88b9a286b17adb5e690286f *inst/include/s2/id_set_lexicon.h
+d1d7c273710a5a6dccb132f810f2ed53 *inst/include/s2/mutable_s2shape_index.h
+a5fd0e84e01c56c79c50f208ae1b08b9 *inst/include/s2/r1interval.h
+e363bde392175774fc26dcde1c3b5076 *inst/include/s2/r2.h
+4b12844b6a0327fbc6f0488b4fbea0b3 *inst/include/s2/r2rect.h
+279bd4aa8ae3c5f5a5b71cd8e4b66c2c *inst/include/s2/s1angle.h
+c3c25817e0a471da972a15c3a4ce8da0 *inst/include/s2/s1chord_angle.h
+2a46bfae65f4a24dbbc0c8a7df478c4c *inst/include/s2/s1interval.h
+99423d6009cb68d7879793f9311e1bb7 *inst/include/s2/s2boolean_operation.h
+f0e436fe4992a1c5f451c08e7be373e8 *inst/include/s2/s2builder.h
+cc61ae8fff8370c728bf6de7513e06d9 *inst/include/s2/s2builder_graph.h
+0558b2bb40025b583974ac1a5d2b5857 *inst/include/s2/s2builder_layer.h
+4b17df092dd21d0d5b22499a7a0d8d63 *inst/include/s2/s2builderutil_closed_set_normalizer.h
+dc965c407adeb3070db3510de3b1f0bd *inst/include/s2/s2builderutil_find_polygon_degeneracies.h
+59390c82ae51ffd8dc8a2154c7f5cc6b *inst/include/s2/s2builderutil_graph_shape.h
+66c64baff11d7ed56ac3e7f93fdd5ac0 *inst/include/s2/s2builderutil_lax_polygon_layer.h
+40506cccfe04e37e54a5bc8f1bde6023 *inst/include/s2/s2builderutil_s2point_vector_layer.h
+cbbba4e593a1ec4c5554b1021939548a *inst/include/s2/s2builderutil_s2polygon_layer.h
+9a36a2942ed405fe0b4b9b2e81bf0a65 *inst/include/s2/s2builderutil_s2polyline_layer.h
+929a066a7fd622cbc8f1a41b2d579962 *inst/include/s2/s2builderutil_s2polyline_vector_layer.h
+14fae38b064b536a50fd99090bf2bb80 *inst/include/s2/s2builderutil_snap_functions.h
+464db150b43db1a4a05c4aef25f9fdb1 *inst/include/s2/s2builderutil_testing.h
+1aaf20b1794973131d85bbd6d16fc296 *inst/include/s2/s2cap.h
+cc050d60fc6b31479ad2cfc444aebc2b *inst/include/s2/s2cell.h
+2ee5d8157ce731621c92127411af51dc *inst/include/s2/s2cell_id.h
+c967db283c31cf3b04720da4aaf951be *inst/include/s2/s2cell_index.h
+ca583789e8fe43d4acea37b55e0f5930 *inst/include/s2/s2cell_union.h
+b0e46b1f9b0302bf28993c83b1c29952 *inst/include/s2/s2centroids.h
+1022d523b50d2186b166ca68dc0deb11 *inst/include/s2/s2closest_cell_query.h
+15e3edaba2c2363568b38435c1ccb49e *inst/include/s2/s2closest_cell_query_base.h
+dca82b1156a1a4c8f587ded132c2f253 *inst/include/s2/s2closest_edge_query.h
+283c65cbbae272d89e5c311ea02297cd *inst/include/s2/s2closest_edge_query_base.h
+0974a6dabc5a7541eebf0e81656e522c *inst/include/s2/s2closest_edge_query_testing.h
+e982b798e56156c65fb5ddf919a0520c *inst/include/s2/s2closest_point_query.h
+838bd8a8fe4cbc3f4828e024d8dc6bae *inst/include/s2/s2closest_point_query_base.h
+c5ac9b7327a1f2eb8c9b8683973bc48c *inst/include/s2/s2contains_point_query.h
+9d28d2f3379f7dea949e76b5e82a3ac9 *inst/include/s2/s2contains_vertex_query.h
+7c2da76e9c9917194d48939ee919f088 *inst/include/s2/s2convex_hull_query.h
+e2a5abdcf611242bdf5cf0edf0eea5ad *inst/include/s2/s2coords.h
+28c4af3631469e232aab685be3e33122 *inst/include/s2/s2coords_internal.h
+3e4f0adaabe0eca3bc103088214106ad *inst/include/s2/s2crossing_edge_query.h
+12e9446c9a552088c70b7b3062c1ba5b *inst/include/s2/s2debug.h
+7298228fc7e23751181ad62e211a81b8 *inst/include/s2/s2distance_target.h
+b90e02812e7d1afa32d15de3a0245dd8 *inst/include/s2/s2earth.h
+5f01c8ad544b54056e6291838925512a *inst/include/s2/s2edge_clipping.h
+91b536b7351c85048ac5f4fc8f9c36f3 *inst/include/s2/s2edge_crosser.h
+a9ac7f4dd56aff0550689af376c3438f *inst/include/s2/s2edge_crossings.h
+482bff39a41ea67b1073b9cfdc11400b *inst/include/s2/s2edge_crossings_internal.h
+525ad6e536828fd0f783a532962bde59 *inst/include/s2/s2edge_distances.h
+0f223cfa5b4b1153617bd64d47f68212 *inst/include/s2/s2edge_tessellator.h
+99195e00ed62fc8127a6de6305730930 *inst/include/s2/s2edge_vector_shape.h
+131a6fb00a28c854c2baadce7716e20e *inst/include/s2/s2error.h
+89b12540c5e64ede6c1101f9d62da052 *inst/include/s2/s2furthest_edge_query.h
+2a746c7606baf6562e51ed195e282afd *inst/include/s2/s2latlng.h
+bd642eda609967162d94ac500632e454 *inst/include/s2/s2latlng_rect.h
+486a11f20722a978644e99ec990516ad *inst/include/s2/s2latlng_rect_bounder.h
+6aaf1e13f42ac06794d6179a45992a12 *inst/include/s2/s2lax_loop_shape.h
+fdd99751ecd044e5de5cbebfb4eca06c *inst/include/s2/s2lax_polygon_shape.h
+4d4f60c49166c8657258de45884c4087 *inst/include/s2/s2lax_polyline_shape.h
+14cced4195983265280bfdbca39b9047 *inst/include/s2/s2loop.h
+eef153a4e7bb190f620c250f78ac7c1c *inst/include/s2/s2loop_measures.h
+ad2641cab77de9fe057639d5afdf4edd *inst/include/s2/s2max_distance_targets.h
+b230afb1b144cfe2dfa5241d0d935cc1 *inst/include/s2/s2measures.h
+c974a90f9d2ed8199af139ad33147aa4 *inst/include/s2/s2metrics.h
+49d8a47ed9a0f58dd97b2258b1764e77 *inst/include/s2/s2min_distance_targets.h
+b2841070cce458abff94905776f74eae *inst/include/s2/s2padded_cell.h
+af4bfede404056bb6ecc81a6ec4ed141 *inst/include/s2/s2point.h
+07518908a403a0b2a797c207fbf41e86 *inst/include/s2/s2point_compression.h
+5fdf216ef50d1d05028f2506491342a5 *inst/include/s2/s2point_index.h
+762457ccfc48a099fb96c63eb2db4829 *inst/include/s2/s2point_region.h
+660d207e2a9aa481fecafd94e234ea6b *inst/include/s2/s2point_span.h
+7d75a6945e15b9c78a713a59bd85f199 *inst/include/s2/s2point_vector_shape.h
+6f748c0d5c03c3eaa0fecab91579975a *inst/include/s2/s2pointutil.h
+cfec075841d3e7114220ef9e0d69e0c3 *inst/include/s2/s2polygon.h
+0a0f0cfcdac7b7dea18ccf795c3c8e11 *inst/include/s2/s2polyline.h
+c0f9b77a9439bc0c84d9dfe2dcbcc25b *inst/include/s2/s2polyline_alignment.h
+eff6795f7a30bf765249022e2fdbab30 *inst/include/s2/s2polyline_alignment_internal.h
+3b78b40b711bc148a7cc8f01f044d9a7 *inst/include/s2/s2polyline_measures.h
+a12b87a494ef6e98dd8b19091ebf8cd8 *inst/include/s2/s2polyline_simplifier.h
+3e580e8e5db3954a6918420df5d50630 *inst/include/s2/s2predicates.h
+e819c0019ab31b8a7772353a762f6439 *inst/include/s2/s2predicates_internal.h
+fa7c158fb73fced84c86e73bc40a4215 *inst/include/s2/s2projections.h
+eef1eb7446a79b5b6430af8eede7a698 *inst/include/s2/s2r2rect.h
+2234c6d0d865cc61ca6959c01e0015e4 *inst/include/s2/s2region.h
+64f8f1de545faf4dcafdb206cfca2bf6 *inst/include/s2/s2region_coverer.h
+bc10b64ffa44cbe8566d7daf5fb025b8 *inst/include/s2/s2region_intersection.h
+24e9c6bee5ce52ebbf1307333d70301a *inst/include/s2/s2region_term_indexer.h
+05b971668fa80d8d89579ad964aa7f27 *inst/include/s2/s2region_union.h
+9e0666e7c03402dbf71fec6da5aa9244 *inst/include/s2/s2shape.h
+5a143a135855c6c2a43d687cedbbc837 *inst/include/s2/s2shape_index.h
+2f6f557e792611c362fee453ca16eb88 *inst/include/s2/s2shape_index_buffered_region.h
+1caf918a11c8bafc0835861870fbd0ea *inst/include/s2/s2shape_index_measures.h
+b095a57fa9ad2adfa3cc31f6b8d6fffd *inst/include/s2/s2shape_index_region.h
+ce9d81e0539f9c3a136e07d12c46cb05 *inst/include/s2/s2shape_measures.h
+36f49316d02ac4f5c1124d7a3365419f *inst/include/s2/s2shapeutil_build_polygon_boundaries.h
+3c7dde9ddf64c467729e08fb30741bbe *inst/include/s2/s2shapeutil_coding.h
+f6d89268f04abb12ccd63236d4362060 *inst/include/s2/s2shapeutil_contains_brute_force.h
+1344b756712c76ac8c390abdb5a5886e *inst/include/s2/s2shapeutil_count_edges.h
+e0937ffaeaa73535cb291409f285f2e3 *inst/include/s2/s2shapeutil_edge_iterator.h
+53160e1333206256c06775aceb56ff6e *inst/include/s2/s2shapeutil_get_reference_point.h
+d12c9628c73beb0f42bb462060facbe7 *inst/include/s2/s2shapeutil_range_iterator.h
+a249fc71604963665d21c311c0e1c391 *inst/include/s2/s2shapeutil_shape_edge.h
+bdf516e73992d5a15fdaa074596f8d6f *inst/include/s2/s2shapeutil_shape_edge_id.h
+007fda069d8144645da7f2c7462a8359 *inst/include/s2/s2shapeutil_testing.h
+f21bfbc04ce2d1d819512176b45954bb *inst/include/s2/s2shapeutil_visit_crossing_edge_pairs.h
+d2af245ac0b0cff65e44c8b28a0c3451 *inst/include/s2/s2testing.h
+388d47b1e65b685972ca35be834ce2c4 *inst/include/s2/s2text_format.h
+3c1f74157144820e8e5008fdc433e018 *inst/include/s2/s2wedge_relations.h
+d3ffa54eb4cab5a70d23bee5cbc17b3e *inst/include/s2/sequence_lexicon.h
+5660849c05e4728e74114429b9b28339 *inst/include/s2/strings/ostringstream.h
+5e58c81561186cabf746091253ece578 *inst/include/s2/strings/serialize.h
+4774a2d59a247bb335bacbe5eb6eb265 *inst/include/s2/third_party/absl/algorithm/algorithm.h
+c5c22df9e5ec5e04e0085325f30268b7 *inst/include/s2/third_party/absl/base/attributes.h
+9055c69eff1250d7b55406f466283b45 *inst/include/s2/third_party/absl/base/casts.h
+e153758883f2d0704a42a3791a7092bd *inst/include/s2/third_party/absl/base/config.h
+5fced460e821fb0e729790dedee70978 *inst/include/s2/third_party/absl/base/dynamic_annotations.h
+b942c8aeb47262065298f7c84599f8ae *inst/include/s2/third_party/absl/base/internal/atomic_hook.h
+0132a8836d59bbc8ebbfd499bced5e57 *inst/include/s2/third_party/absl/base/internal/identity.h
+cacec291159c34faf2124f02ae587149 *inst/include/s2/third_party/absl/base/internal/inline_variable.h
+215c83c3b57dc7299452971b5751111b *inst/include/s2/third_party/absl/base/internal/invoke.h
+a1544a26ecf4f13213df38bfa8ca2ec1 *inst/include/s2/third_party/absl/base/internal/raw_logging.h
+95a923cf95eb8115790ea3aa39ba4544 *inst/include/s2/third_party/absl/base/internal/throw_delegate.h
+2eb0626e04425567d155077fd975f454 *inst/include/s2/third_party/absl/base/internal/unaligned_access.h
+c3dcb836940be43411fd4c4890bc649e *inst/include/s2/third_party/absl/base/log_severity.h
+ae09d12a126842ad529872c06130bd23 *inst/include/s2/third_party/absl/base/macros.h
+2de40013561322141fd61eb7f8e7939f *inst/include/s2/third_party/absl/base/optimization.h
+a62aa9b3fa1d55d8e959929984065260 *inst/include/s2/third_party/absl/base/policy_checks.h
+4ec15da9fbdf62615835e19927c81c9d *inst/include/s2/third_party/absl/base/port.h
+c1eef28bafed8aea62144ee837a81c7d *inst/include/s2/third_party/absl/base/thread_annotations.h
+ba3327fbea8fed70386f15a0e898a3fa *inst/include/s2/third_party/absl/container/fixed_array.h
+e12a61a76d8ed6380d17d3c6293dfb5d *inst/include/s2/third_party/absl/container/inlined_vector.h
+4fa2041669639f60524e2191a528b058 *inst/include/s2/third_party/absl/container/internal/compressed_tuple.h
+720e6131f877ba385b4174e4e3ca5a95 *inst/include/s2/third_party/absl/container/internal/container_memory.h
+c8d7b706e67864565bc035dfe6735d2c *inst/include/s2/third_party/absl/container/internal/layout.h
+c3e6cf276acd1ac325e8d5f5134511c5 *inst/include/s2/third_party/absl/memory/memory.h
+cf8a2d3e5aea9659c5cda43962a5bda9 *inst/include/s2/third_party/absl/meta/type_traits.h
+a429f3aaa1ed1c847ea9c9ae7530ec0e *inst/include/s2/third_party/absl/numeric/int128.h
+95dc4b1604f6514051107ef12c4084cd *inst/include/s2/third_party/absl/numeric/int128_have_intrinsic.inc
+0af74de0605ecb52abf0a99f6c4f375c *inst/include/s2/third_party/absl/numeric/int128_no_intrinsic.inc
+90d00026063ac43ffbc93066ff6df857 *inst/include/s2/third_party/absl/strings/ascii.h
+5261e3adf5c680466c5c8099fb515cec *inst/include/s2/third_party/absl/strings/ascii_ctype.h
+a3c35c225cf76eaabcee8b7f24f72a7c *inst/include/s2/third_party/absl/strings/internal/bits.h
+e8699c817825346c1d849332fd3ccdb2 *inst/include/s2/third_party/absl/strings/internal/memutil.h
+c2f0428da28989afc38bc590bbacb2ef *inst/include/s2/third_party/absl/strings/internal/resize_uninitialized.h
+14f2714ff679c7ef2777b7021290c7af *inst/include/s2/third_party/absl/strings/match.h
+4f3281e25b414a4b244835e228173c36 *inst/include/s2/third_party/absl/strings/numbers.h
+408b2c9f4079600fd721ceb4ad1a4403 *inst/include/s2/third_party/absl/strings/str_cat.h
+20d77d9f1d45146178390b640f963653 *inst/include/s2/third_party/absl/strings/str_join.h
+af303dc872a7afff5c4b7d9213609ecc *inst/include/s2/third_party/absl/strings/str_split.h
+58505d1fe7175e0ec054c0bf8c9e7417 *inst/include/s2/third_party/absl/strings/string_view.h
+27833d1d0b3809492956eaab1f767c08 *inst/include/s2/third_party/absl/strings/strip.h
+ff057e84f9c7d95441204c08a99b6624 *inst/include/s2/third_party/absl/types/span.h
+4b112f693a7fe580bfdcc0294bdaa16d *inst/include/s2/third_party/absl/utility/utility.h
+3bfcb45aa9bfa89827d30afba984f79c *inst/include/s2/util/bits/bit-interleave.h
+01651f0b0520524dbb4584decd493d3b *inst/include/s2/util/bits/bits.h
+5f6a88e3427ce3578a61423ab34608d5 *inst/include/s2/util/coding/coder.h
+c4fe67fd8a294e03bfc46ce9fb1134d1 *inst/include/s2/util/coding/nth-derivative.h
+2e711fab16d3ec015ecc12d522b8b0ae *inst/include/s2/util/coding/transforms.h
+c5e3e09198b1b08e701f3776454f01dd *inst/include/s2/util/coding/varint.h
+d02fbefd15c14ec0bd673853a526bfc8 *inst/include/s2/util/endian/endian.h
+0d177612b828c957b49d097e94a28b32 *inst/include/s2/util/gtl/btree.h
+fb878c4cc124f8cd73ff043b9fe9e278 *inst/include/s2/util/gtl/btree_container.h
+52e6fdc3fa81363e043d7e5a35a697ef *inst/include/s2/util/gtl/btree_map.h
+81e2de007193adce24cd9d5f5739bf5e *inst/include/s2/util/gtl/btree_set.h
+29d3a30325dc78051b3aa1b812fb71c7 *inst/include/s2/util/gtl/compact_array.h
+a05aa6118ec536e2715ce0089096d564 *inst/include/s2/util/gtl/container_logging.h
+524152483057734236c014aaf39cabe3 *inst/include/s2/util/gtl/dense_hash_set.h
+e6d5083ac705a0300e77fe096a7a0589 *inst/include/s2/util/gtl/densehashtable.h
+fa56660fc40be26b3dbfc7115c56674e *inst/include/s2/util/gtl/hashtable_common.h
+a899a9f61f6eff376a230da0cf6ed16a *inst/include/s2/util/gtl/layout.h
+f1dc5e28aa905fbf4af9530f7cf14111 *inst/include/s2/util/gtl/legacy_random_shuffle.h
+a36fbccb7e3c064e5a16b4ac1e7842f3 *inst/include/s2/util/hash/mix.h
+b0a3631c4d2beb9b19d9e3a0639c0cf0 *inst/include/s2/util/math/exactfloat/exactfloat.h
+0c3be461d027e0cccb9c0facb12cd046 *inst/include/s2/util/math/mathutil.h
+160830f8d1d1983f1b773f953abff72d *inst/include/s2/util/math/matrix3x3.h
+54ea617707ee52e966ce4e475067ec28 *inst/include/s2/util/math/vector.h
+5b091051cad1329e1e2b535e40e9c142 *inst/include/s2/util/math/vector3_hash.h
+1bdf37d9bef7eba2b40d5baad2403f8d *inst/include/s2/util/units/length-units.h
+a5add3e948f775d2f77331e97b5398ea *inst/include/s2/util/units/physical-units.h
+400af05643ac08d897c2100a0a68f1aa *inst/include/s2/value_lexicon.h
+3c3a7db94dc9033c9c0c6ef7d6d0c8a4 *man/as_s2_geography.Rd
+ed01c59b91c1a9c3382d1f2ab67e60cc *man/figures/rc300.png
+0d32af4cce70305a4ad68adb26e7c89f *man/s2-package.Rd
+0814caa3efd1d23d1a02cd3da15d3424 *man/s2_boundary.Rd
+a906d7f9956940ceebe0bd879c14e5f2 *man/s2_bounds_cap.Rd
+74e5a897578b3e75ccafbd46bcd3e2fa *man/s2_closest_feature.Rd
+d96650f409fa8eb02fe4d5f54d00eb7d *man/s2_contains.Rd
+addd614791ada510339317f68202d348 *man/s2_data_tbl_countries.Rd
+d7a1ee72a69458a577921e02c2f4142f *man/s2_earth_radius_meters.Rd
+b1c82724ddfd755007942e7763d7b333 *man/s2_geog_point.Rd
+022dbf15f8934cc4accf0c41a55a4a44 *man/s2_is_collection.Rd
+e1a47506a896f0144af3c34265fef2b1 *man/s2_lnglat.Rd
+22b3d3bc9001655c786bd6515fe874d2 *man/s2_options.Rd
+a86f2581e1af6031167a29956341993c *man/s2_point.Rd
+7928b4cf96caa1ee6349d575e7d5899f *src/Makevars.in
+c7a379bba69903eac2145a7b5e0b7be1 *src/Makevars.win
+294dd54a661879e3713ca07fff5dc459 *src/RcppExports.cpp
+5f46d29bc94bcdfc28e2a4df442310de *src/cpp-compat.cpp
+fdce8bafee6053b81f1b8c8039b41493 *src/geography-collection.h
+8f89404ccd6210e21b6b5b29867597ab *src/geography-operator.h
+a7705e137e495ff2a4e380556763c555 *src/geography.h
+0365ec85c22784d83cce5b84d71d794a *src/init.cpp
+e2fbee45aca698d82ddac432ada8b39a *src/point-geography.h
+82b010ba5876d13494164c57f7125a03 *src/polygon-geography.h
+025a71734811c66df0767952306ff45b *src/polyline-geography.h
+5ad00dddb331ad9ab3528b330c3e9a05 *src/s2-accessors.cpp
+40989d3a6148de2a42117013f098d21c *src/s2-bounds.cpp
+fb1fff2046ade79c266428478bceb49b *src/s2-constructors-formatters.cpp
+bc596d9aaa251dd5884d1537b0630fc6 *src/s2-geography.cpp
+2a18a33f6a16832c371549cee3ef1829 *src/s2-lnglat.cpp
+27938e376941d899bf9052fcfe51e366 *src/s2-matrix.cpp
+3faf84f0e15b9e9807e2c13d7a81a031 *src/s2-options.h
+26da724d2fb9ccb3b7579e9a7f49001e *src/s2-point.cpp
+980e286c3b8ad05def8cb5729e83a98f *src/s2-predicates.cpp
+e66dde63f2c8c100f82291f25712bc9e *src/s2-transformers.cpp
+2a3ac9aacf9ff1513a19f70b91902c83 *src/s2-xptr.cpp
+5870223619be64e10cd385cd100ba1e0 *src/s2/base/stringprintf.cc
+c10c3c7ca58239c0915bacffc70e37e1 *src/s2/base/strtoint.cc
+a85315a801220cea6aedb7545bed0675 *src/s2/encoded_s2cell_id_vector.cc
+46fc24762b4540e4fca69d7ae755de0c *src/s2/encoded_s2point_vector.cc
+ef7ca1699c3646abc615e0fdf400f10d *src/s2/encoded_s2shape_index.cc
+2a2a38594d08c617c2b2a277e278e320 *src/s2/encoded_string_vector.cc
+788f48e5bbfca22ed1e396d4bd675ad3 *src/s2/id_set_lexicon.cc
+e77194370c1950bc155ee61708af57b0 *src/s2/mutable_s2shape_index.cc
+da84b9bfc2aeabd4f8ae0f84343623e7 *src/s2/r2rect.cc
+e7ca7f23a7e8fe6f707e4169f1aadb77 *src/s2/s1angle.cc
+d9387a9af7477f6b82ec73d206cfd8d9 *src/s2/s1chord_angle.cc
+34874873bee5edbf9af38cd7692ab3ae *src/s2/s1interval.cc
+511d35103ca70d436f998e8a02aac9c5 *src/s2/s2boolean_operation.cc
+451752fc8769396106cc1a751942673a *src/s2/s2builder.cc
+13695f3a4b75e18becb5d3828e81da65 *src/s2/s2builder_graph.cc
+840316e7a7b185fc36824c2fefdce8d3 *src/s2/s2builderutil_closed_set_normalizer.cc
+af468682dc5753112e46f2ace3393cb7 *src/s2/s2builderutil_find_polygon_degeneracies.cc
+93c788e6753481cf0ac93247a1349b62 *src/s2/s2builderutil_lax_polygon_layer.cc
+6c0c3e9f73a2d693d25392a6f053cca9 *src/s2/s2builderutil_s2point_vector_layer.cc
+d38e7886a72b3ddceec6fe97cbe020b0 *src/s2/s2builderutil_s2polygon_layer.cc
+bc24648fc1ba139430b08df68cc45004 *src/s2/s2builderutil_s2polyline_layer.cc
+9ed299b94f9fd05d3b228cbbb77a29e9 *src/s2/s2builderutil_s2polyline_vector_layer.cc
+0db1a581191eee0bdbbb3f889994453d *src/s2/s2builderutil_snap_functions.cc
+0e7eb31b407e02950751a990c528833d *src/s2/s2builderutil_testing.cc
+15661d854ecdbd9b5adb37f4fa38e7fd *src/s2/s2cap.cc
+d89508276ed169153d1e80d85989bfd5 *src/s2/s2cell.cc
+593d094df40de93980e92961647b8660 *src/s2/s2cell_id.cc
+b77c66885dd9ce9decb058448a53bf2b *src/s2/s2cell_index.cc
+2df8fa84fabd7e103ecbcf3dddec4ab7 *src/s2/s2cell_union.cc
+cf1a07e72197832f2b1302f7b3630f98 *src/s2/s2centroids.cc
+247c9902f0b40b738f8636d6436fbdb6 *src/s2/s2closest_cell_query.cc
+bed4f6b983774f4c6b4a6596ad46ee08 *src/s2/s2closest_edge_query.cc
+09e2f693ad6e491a4d50eb8b5d966840 *src/s2/s2closest_point_query.cc
+8cea937d5d1881cdb71d1287f5474cff *src/s2/s2contains_vertex_query.cc
+6122ec2910ab45960634d81f2058a9e0 *src/s2/s2convex_hull_query.cc
+effdbbf64bb7f235381201657e021ea6 *src/s2/s2coords.cc
+48655b45fa96f0da0b234ca68fe4b54d *src/s2/s2crossing_edge_query.cc
+cbeeb65a7261dc8430dcf6edc24deb94 *src/s2/s2debug.cc
+a863fc668a334819762480cc0646c898 *src/s2/s2earth.cc
+d58b020152efbe99f672b8952460d2ce *src/s2/s2edge_clipping.cc
+c5fd68f5fab9c30285a9d1995be3ddde *src/s2/s2edge_crosser.cc
+69bb6ff8072fe88be08b2e693071ea65 *src/s2/s2edge_crossings.cc
+2afc2b3b5d5c5eee69d662c490aeac3a *src/s2/s2edge_distances.cc
+75acbcdde0e718a3a990fa63c78c3423 *src/s2/s2edge_tessellator.cc
+483c797be9c9eb4b9a9059577e548e7b *src/s2/s2error.cc
+fe0c39101c8c58f45b75fc4c36261a82 *src/s2/s2furthest_edge_query.cc
+8f0404619c8c69989e81a9eafb8abe68 *src/s2/s2latlng.cc
+e42ec23a4fdf8b6aea983db2ca3cc4d9 *src/s2/s2latlng_rect.cc
+f8689b6a3eef9053601d310ca6146f5c *src/s2/s2latlng_rect_bounder.cc
+6ea59dae602c6e87765068c7c4357baf *src/s2/s2lax_loop_shape.cc
+fc760f9c0ff8545929b4a1e0da1e6ff5 *src/s2/s2lax_polygon_shape.cc
+56c7b3c6c942d0cffe353b03ee605a8f *src/s2/s2lax_polyline_shape.cc
+e9089ad010d3996decf00ad060e2c9b1 *src/s2/s2loop.cc
+436911cd7d5a6dcc5fd8b5415c6e0603 *src/s2/s2loop_measures.cc
+ca46f698320fb52b1a5a963c3dda9584 *src/s2/s2max_distance_targets.cc
+66d7e4a5a3700d4b5a8c7e013fcfc370 *src/s2/s2measures.cc
+b70ef8989674552ca3d0e0c9fcb754a8 *src/s2/s2metrics.cc
+03adc6de0eef0e8578423e8fadd5542f *src/s2/s2min_distance_targets.cc
+94bd23bc709062647f0bdc993dfc7933 *src/s2/s2padded_cell.cc
+ebcad838fe84cee7d007143e3cbac687 *src/s2/s2point_compression.cc
+7b88d7113b91cd8d5f0c1ad5bbacc7c4 *src/s2/s2point_region.cc
+0475a05e50a35ca03b06e084178cd31a *src/s2/s2pointutil.cc
+eacacbc76f611ff5f249666746bb414a *src/s2/s2polygon.cc
+4a91c95c87427a46f759ca2f469f38b5 *src/s2/s2polyline.cc
+52d66d8e55dab56d35a33029e25182ce *src/s2/s2polyline_alignment.cc
+c206c2fa52e4a5588b1024f45c63595b *src/s2/s2polyline_measures.cc
+eab00b14b7643e320f8044521eaa16e4 *src/s2/s2polyline_simplifier.cc
+ff7549a0d6195b34b24867fff4cc6d06 *src/s2/s2predicates.cc
+7478e1714d9dcaf520159458d10e383b *src/s2/s2projections.cc
+8baa87c45aa11148a5207f367237e753 *src/s2/s2r2rect.cc
+57778c7c84ae75e67ed68e76f9026ec0 *src/s2/s2region.cc
+cfc92b8d78c68ae15377d9e1c5a8df13 *src/s2/s2region_coverer.cc
+f622c31665d97c607e1cb5106c295959 *src/s2/s2region_intersection.cc
+b41c960fdbefe867c01ee524e3929d3f *src/s2/s2region_term_indexer.cc
+f8232c70a590ed11666c93181c9d7343 *src/s2/s2region_union.cc
+25ac091ec0963d3216b86e86a050eae9 *src/s2/s2shape_index.cc
+906e1e16bc8aa9f5d4e99651a6cdf0fe *src/s2/s2shape_index_buffered_region.cc
+c0543285953743a9605aa3ef63ad6127 *src/s2/s2shape_index_measures.cc
+23c94c388142e68594cbf29b677d2bbc *src/s2/s2shape_measures.cc
+a71d6a318728f4f4d425a283fe298eb2 *src/s2/s2shapeutil_build_polygon_boundaries.cc
+dbe0790cef156dde776484cacc699485 *src/s2/s2shapeutil_coding.cc
+afd2720bf07ba0aa5f94dfe9ba0c4d3a *src/s2/s2shapeutil_contains_brute_force.cc
+5c5fc095963d0f42ddbb64977d5d267f *src/s2/s2shapeutil_edge_iterator.cc
+ae2b2addb5cb94cd24c57c195e1b0b49 *src/s2/s2shapeutil_get_reference_point.cc
+6d1353bb32206db05048ee50a073c0e7 *src/s2/s2shapeutil_range_iterator.cc
+2d43581ce7e9638ee9d4390016d422df *src/s2/s2shapeutil_visit_crossing_edge_pairs.cc
+969daf687171d174fbe727b6727f65bb *src/s2/s2testing.cc
+40f4712317f6b98e06a78be740f60409 *src/s2/s2text_format.cc
+518f25738bd53f6439d3708f56a85435 *src/s2/s2wedge_relations.cc
+9b6952846f960ce7c8f86d425a6cfc53 *src/s2/strings/ostringstream.cc
+eca9409a0404788acbef02f2fd4f51df *src/s2/strings/serialize.cc
+ed362e76c37bc3b132df67914ff1ae63 *src/s2/third_party/absl/base/dynamic_annotations.cc
+1bf91ab480aa123b927f4b3873f3cb82 *src/s2/third_party/absl/base/internal/raw_logging.cc
+c894340e8006bfa71425539409d16115 *src/s2/third_party/absl/base/internal/throw_delegate.cc
+238a3a6f859b344e23ebf1e8215d2824 *src/s2/third_party/absl/numeric/int128.cc
+d87d99e4a3010c3ada6f90dcd0f9610f *src/s2/third_party/absl/strings/ascii.cc
+77bb965ed3aca0c14e26b379046729ac *src/s2/third_party/absl/strings/internal/memutil.cc
+918e0ac8d6b33177ac95bb9cbddb8ffe *src/s2/third_party/absl/strings/match.cc
+07e47a54105f2a19e1955ecbc10f8ba3 *src/s2/third_party/absl/strings/numbers.cc
+b2ded99aff2adda60c7936c80e01580e *src/s2/third_party/absl/strings/str_cat.cc
+b022ff16e439872f0bb3a608194481c4 *src/s2/third_party/absl/strings/str_split.cc
+4877473e8e04e7f4c25a0c7dda833641 *src/s2/third_party/absl/strings/string_view.cc
+04a1c7524bf937976acbdd7a63a298b9 *src/s2/third_party/absl/strings/strip.cc
+c90e431dac905cc9e596e1b1071b7ea1 *src/s2/util/bits/bit-interleave.cc
+7fce87b2c7658b46bea4fd2096c902fb *src/s2/util/bits/bits.cc
+c0d70edbf2ac3f698c17d89959e326f9 *src/s2/util/coding/coder.cc
+b1348c0fb971b3302725b248a6299581 *src/s2/util/coding/varint.cc
+2fc5ca8d2efaf82b72d5120a102e1aa0 *src/s2/util/math/exactfloat/exactfloat.cc
+2037118f434c6f41bc8014de2eb18c92 *src/s2/util/math/mathutil.cc
+2aba55dd12417d944cd91a2f02b1d946 *src/s2/util/units/length-units.cc
+4495750656420f7cd041bf0bf27ec020 *src/tests/soname.h
+d105b4c3460bf21c7c7ef886eaaa29d2 *src/wk-geography.h
+a9ffa55303a6dcba9c5c9c9d322e2009 *tests/area.R
+960ec65d25d1018d2084d81ed3324fdd *tests/area.Rout
+6a0d9b8264490c375aab2554285f41ec *tests/testthat.R
+2852576d235eb67783d7b1118d8d9800 *tests/testthat/test-data.R
+5515aee1bf7a1360c0f1de84a6b58eeb *tests/testthat/test-s2-accessors.R
+1047ccf796c487587c03286eb8ad3eb2 *tests/testthat/test-s2-bounds.R
+cfb96b2f1b55571dd58adcbeb254950f *tests/testthat/test-s2-constructors-formatters.R
+75ad7009cf6227ca8239c31198787956 *tests/testthat/test-s2-earth.R
+7a6af524d3f7aadc667753dd5eea7548 *tests/testthat/test-s2-geography.R
+adfd5e8008ceeea58ba3ee5e71ca040b *tests/testthat/test-s2-lnglat.R
+7b587e0c348fd70e7f1308e33f48aac9 *tests/testthat/test-s2-matrix.R
+589616aabb1c291a3984f521274815bf *tests/testthat/test-s2-options.R
+9be8d6955daa3c211af562f4f41c639a *tests/testthat/test-s2-point.R
+0688d88be5a5f999507d227ee30c99dc *tests/testthat/test-s2-predicates.R
+b7505b66cb1b1b254137ebd00c86c225 *tests/testthat/test-s2-transformers.R
+e0de43269c8f8eb622ef3aa6c6d6f01a *tests/testthat/test-s2-xptr.R
+28f67bf7ece751b0003f0cb923fefa24 *tests/testthat/test-utils.R
+960c0d7e6a792c605d49ccfe2ac6a968 *tests/testthat/test-vctrs.R
+0ec27c181a66ce5b72bc8c3940e9e00e *tools/version.c
+c649ea1343c76b81870be0fa55f6feb3 *tools/winlibs.R
--- /dev/null
+# Generated by roxygen2: do not edit by hand
+
+S3method("[",s2_xptr)
+S3method("[<-",s2_geography)
+S3method("[<-",s2_lnglat)
+S3method("[<-",s2_point)
+S3method("[[",s2_xptr)
+S3method("[[<-",s2_geography)
+S3method("[[<-",s2_lnglat)
+S3method("[[<-",s2_point)
+S3method(as.character,s2_geography)
+S3method(as.data.frame,s2_lnglat)
+S3method(as.data.frame,s2_point)
+S3method(as.matrix,s2_lnglat)
+S3method(as.matrix,s2_point)
+S3method(as_s2_geography,WKB)
+S3method(as_s2_geography,blob)
+S3method(as_s2_geography,character)
+S3method(as_s2_geography,default)
+S3method(as_s2_geography,logical)
+S3method(as_s2_geography,s2_geography)
+S3method(as_s2_geography,s2_lnglat)
+S3method(as_s2_geography,s2_point)
+S3method(as_s2_geography,wk_wkb)
+S3method(as_s2_geography,wk_wkt)
+S3method(as_s2_lnglat,character)
+S3method(as_s2_lnglat,matrix)
+S3method(as_s2_lnglat,s2_geography)
+S3method(as_s2_lnglat,s2_lnglat)
+S3method(as_s2_lnglat,s2_point)
+S3method(as_s2_lnglat,wk_wkb)
+S3method(as_s2_lnglat,wk_wkt)
+S3method(as_s2_point,matrix)
+S3method(as_s2_point,s2_geography)
+S3method(as_s2_point,s2_lnglat)
+S3method(as_s2_point,s2_point)
+S3method(as_wkb,s2_geography)
+S3method(as_wkb,s2_lnglat)
+S3method(as_wkt,s2_geography)
+S3method(as_wkt,s2_lnglat)
+S3method(c,s2_xptr)
+S3method(format,s2_geography)
+S3method(format,s2_lnglat)
+S3method(format,s2_point)
+S3method(print,s2_xptr)
+S3method(rep,s2_xptr)
+S3method(rep_len,s2_xptr)
+export(as_s2_geography)
+export(as_s2_lnglat)
+export(as_s2_point)
+export(s2_area)
+export(s2_as_binary)
+export(s2_as_text)
+export(s2_boundary)
+export(s2_bounds_cap)
+export(s2_bounds_rect)
+export(s2_buffer_cells)
+export(s2_centroid)
+export(s2_centroid_agg)
+export(s2_closest_feature)
+export(s2_closest_point)
+export(s2_contains)
+export(s2_contains_matrix)
+export(s2_covered_by)
+export(s2_covered_by_matrix)
+export(s2_covers)
+export(s2_covers_matrix)
+export(s2_data_cities)
+export(s2_data_countries)
+export(s2_data_timezones)
+export(s2_difference)
+export(s2_dimension)
+export(s2_disjoint)
+export(s2_disjoint_matrix)
+export(s2_distance)
+export(s2_distance_matrix)
+export(s2_dwithin)
+export(s2_dwithin_matrix)
+export(s2_earth_radius_meters)
+export(s2_equals)
+export(s2_equals_matrix)
+export(s2_farthest_feature)
+export(s2_geog_from_text)
+export(s2_geog_from_wkb)
+export(s2_geog_point)
+export(s2_intersection)
+export(s2_intersects)
+export(s2_intersects_box)
+export(s2_intersects_matrix)
+export(s2_is_collection)
+export(s2_is_empty)
+export(s2_length)
+export(s2_lnglat)
+export(s2_make_line)
+export(s2_make_polygon)
+export(s2_max_distance)
+export(s2_max_distance_matrix)
+export(s2_may_intersect_matrix)
+export(s2_minimum_clearance_line_between)
+export(s2_num_points)
+export(s2_options)
+export(s2_perimeter)
+export(s2_point)
+export(s2_rebuild)
+export(s2_simplify)
+export(s2_snap_distance)
+export(s2_snap_identity)
+export(s2_snap_level)
+export(s2_snap_precision)
+export(s2_snap_to_grid)
+export(s2_sym_difference)
+export(s2_touches)
+export(s2_touches_matrix)
+export(s2_union)
+export(s2_union_agg)
+export(s2_within)
+export(s2_within_matrix)
+export(s2_x)
+export(s2_y)
+importFrom(Rcpp,sourceCpp)
+importFrom(wk,as_wkb)
+importFrom(wk,as_wkt)
+useDynLib(s2, .registration = TRUE)
--- /dev/null
+# s2 1.0.4
+
+* Fixed errors that resulted from compilation on clang 12.2 (#88, #89).
+
+# s2 1.0.3
+
+* Fixed CRAN check errors (#80).
+
+# s2 1.0.2
+
+* Fixed CRAN check errors (#71, #75, #72).
+
+# s2 1.0.1
+
+* Added layer creation options to `s2_options()`, which now uses strings
+ rather than numeric codes to specify boolean operation options, geography
+ construction options, and builder options (#70).
+* Added `s2_rebuild()` and `s2_simplify()`, which wrap the S2 C++ `S2Builder`
+ class to provide simplification and fixing of invalid geographies (#70).
+* The s2 package now builds and passes the CMD check on Solaris (#66, #67).
+* Renamed `s2_latlng()` to `s2_lnglat()` to keep axis order consistent
+ throughout the package (#69).
+* Added `s2_bounds_cap()` and `s2_bounds_rect()` to compute bounding areas
+ using geographic coordinates (@edzer, #63).
+* `s2_*_matrix()` predicates now efficiently use indexing to compute the
+ results of many predicate comparisons (#61).
+
+# s2 1.0.0
+
+This version is a complete rewrite of the former s2 CRAN package, entirely
+backwards incompatible with previous versions.
--- /dev/null
+# Generated by using Rcpp::compileAttributes() -> do not edit by hand
+# Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393
+
+cpp_s2_init <- function() {
+ invisible(.Call(`_s2_cpp_s2_init`))
+}
+
+cpp_s2_is_collection <- function(geog) {
+ .Call(`_s2_cpp_s2_is_collection`, geog)
+}
+
+cpp_s2_dimension <- function(geog) {
+ .Call(`_s2_cpp_s2_dimension`, geog)
+}
+
+cpp_s2_num_points <- function(geog) {
+ .Call(`_s2_cpp_s2_num_points`, geog)
+}
+
+cpp_s2_is_empty <- function(geog) {
+ .Call(`_s2_cpp_s2_is_empty`, geog)
+}
+
+cpp_s2_area <- function(geog) {
+ .Call(`_s2_cpp_s2_area`, geog)
+}
+
+cpp_s2_length <- function(geog) {
+ .Call(`_s2_cpp_s2_length`, geog)
+}
+
+cpp_s2_perimeter <- function(geog) {
+ .Call(`_s2_cpp_s2_perimeter`, geog)
+}
+
+cpp_s2_x <- function(geog) {
+ .Call(`_s2_cpp_s2_x`, geog)
+}
+
+cpp_s2_y <- function(geog) {
+ .Call(`_s2_cpp_s2_y`, geog)
+}
+
+cpp_s2_distance <- function(geog1, geog2) {
+ .Call(`_s2_cpp_s2_distance`, geog1, geog2)
+}
+
+cpp_s2_max_distance <- function(geog1, geog2) {
+ .Call(`_s2_cpp_s2_max_distance`, geog1, geog2)
+}
+
+cpp_s2_bounds_cap <- function(geog) {
+ .Call(`_s2_cpp_s2_bounds_cap`, geog)
+}
+
+cpp_s2_bounds_rect <- function(geog) {
+ .Call(`_s2_cpp_s2_bounds_rect`, geog)
+}
+
+cpp_s2_geog_point <- function(x, y) {
+ .Call(`_s2_cpp_s2_geog_point`, x, y)
+}
+
+cpp_s2_make_line <- function(x, y, featureId) {
+ .Call(`_s2_cpp_s2_make_line`, x, y, featureId)
+}
+
+cpp_s2_make_polygon <- function(x, y, featureId, ringId, oriented, check) {
+ .Call(`_s2_cpp_s2_make_polygon`, x, y, featureId, ringId, oriented, check)
+}
+
+s2_geography_from_wkb <- function(wkb, oriented, check) {
+ .Call(`_s2_s2_geography_from_wkb`, wkb, oriented, check)
+}
+
+s2_geography_from_wkt <- function(wkt, oriented, check) {
+ .Call(`_s2_s2_geography_from_wkt`, wkt, oriented, check)
+}
+
+s2_geography_full <- function(x) {
+ .Call(`_s2_s2_geography_full`, x)
+}
+
+s2_geography_to_wkt <- function(s2_geography, precision, trim) {
+ .Call(`_s2_s2_geography_to_wkt`, s2_geography, precision, trim)
+}
+
+s2_geography_to_wkb <- function(s2_geography, endian) {
+ .Call(`_s2_s2_geography_to_wkb`, s2_geography, endian)
+}
+
+s2_geography_format <- function(s2_geography, maxCoords, precision, trim) {
+ .Call(`_s2_s2_geography_format`, s2_geography, maxCoords, precision, trim)
+}
+
+s2_lnglat_from_numeric <- function(lng, lat) {
+ .Call(`_s2_s2_lnglat_from_numeric`, lng, lat)
+}
+
+s2_lnglat_from_s2_point <- function(s2_point) {
+ .Call(`_s2_s2_lnglat_from_s2_point`, s2_point)
+}
+
+data_frame_from_s2_lnglat <- function(xptr) {
+ .Call(`_s2_data_frame_from_s2_lnglat`, xptr)
+}
+
+cpp_s2_closest_feature <- function(geog1, geog2) {
+ .Call(`_s2_cpp_s2_closest_feature`, geog1, geog2)
+}
+
+cpp_s2_farthest_feature <- function(geog1, geog2) {
+ .Call(`_s2_cpp_s2_farthest_feature`, geog1, geog2)
+}
+
+cpp_s2_may_intersect_matrix <- function(geog1, geog2, maxEdgesPerCell, maxFeatureCells, s2options) {
+ .Call(`_s2_cpp_s2_may_intersect_matrix`, geog1, geog2, maxEdgesPerCell, maxFeatureCells, s2options)
+}
+
+cpp_s2_contains_matrix <- function(geog1, geog2, s2options) {
+ .Call(`_s2_cpp_s2_contains_matrix`, geog1, geog2, s2options)
+}
+
+cpp_s2_within_matrix <- function(geog1, geog2, s2options) {
+ .Call(`_s2_cpp_s2_within_matrix`, geog1, geog2, s2options)
+}
+
+cpp_s2_intersects_matrix <- function(geog1, geog2, s2options) {
+ .Call(`_s2_cpp_s2_intersects_matrix`, geog1, geog2, s2options)
+}
+
+cpp_s2_equals_matrix <- function(geog1, geog2, s2options) {
+ .Call(`_s2_cpp_s2_equals_matrix`, geog1, geog2, s2options)
+}
+
+cpp_s2_touches_matrix <- function(geog1, geog2, s2options) {
+ .Call(`_s2_cpp_s2_touches_matrix`, geog1, geog2, s2options)
+}
+
+cpp_s2_dwithin_matrix <- function(geog1, geog2, distance) {
+ .Call(`_s2_cpp_s2_dwithin_matrix`, geog1, geog2, distance)
+}
+
+cpp_s2_distance_matrix <- function(geog1, geog2) {
+ .Call(`_s2_cpp_s2_distance_matrix`, geog1, geog2)
+}
+
+cpp_s2_max_distance_matrix <- function(geog1, geog2) {
+ .Call(`_s2_cpp_s2_max_distance_matrix`, geog1, geog2)
+}
+
+cpp_s2_contains_matrix_brute_force <- function(geog1, geog2, s2options) {
+ .Call(`_s2_cpp_s2_contains_matrix_brute_force`, geog1, geog2, s2options)
+}
+
+cpp_s2_within_matrix_brute_force <- function(geog1, geog2, s2options) {
+ .Call(`_s2_cpp_s2_within_matrix_brute_force`, geog1, geog2, s2options)
+}
+
+cpp_s2_intersects_matrix_brute_force <- function(geog1, geog2, s2options) {
+ .Call(`_s2_cpp_s2_intersects_matrix_brute_force`, geog1, geog2, s2options)
+}
+
+cpp_s2_disjoint_matrix_brute_force <- function(geog1, geog2, s2options) {
+ .Call(`_s2_cpp_s2_disjoint_matrix_brute_force`, geog1, geog2, s2options)
+}
+
+cpp_s2_equals_matrix_brute_force <- function(geog1, geog2, s2options) {
+ .Call(`_s2_cpp_s2_equals_matrix_brute_force`, geog1, geog2, s2options)
+}
+
+s2_point_from_numeric <- function(x, y, z) {
+ .Call(`_s2_s2_point_from_numeric`, x, y, z)
+}
+
+s2_point_from_s2_lnglat <- function(s2_lnglat) {
+ .Call(`_s2_s2_point_from_s2_lnglat`, s2_lnglat)
+}
+
+data_frame_from_s2_point <- function(s2_point) {
+ .Call(`_s2_data_frame_from_s2_point`, s2_point)
+}
+
+cpp_s2_intersects <- function(geog1, geog2, s2options) {
+ .Call(`_s2_cpp_s2_intersects`, geog1, geog2, s2options)
+}
+
+cpp_s2_equals <- function(geog1, geog2, s2options) {
+ .Call(`_s2_cpp_s2_equals`, geog1, geog2, s2options)
+}
+
+cpp_s2_contains <- function(geog1, geog2, s2options) {
+ .Call(`_s2_cpp_s2_contains`, geog1, geog2, s2options)
+}
+
+cpp_s2_touches <- function(geog1, geog2, s2options) {
+ .Call(`_s2_cpp_s2_touches`, geog1, geog2, s2options)
+}
+
+cpp_s2_dwithin <- function(geog1, geog2, distance) {
+ .Call(`_s2_cpp_s2_dwithin`, geog1, geog2, distance)
+}
+
+cpp_s2_intersects_box <- function(geog, lng1, lat1, lng2, lat2, detail, s2options) {
+ .Call(`_s2_cpp_s2_intersects_box`, geog, lng1, lat1, lng2, lat2, detail, s2options)
+}
+
+cpp_s2_intersection <- function(geog1, geog2, s2options) {
+ .Call(`_s2_cpp_s2_intersection`, geog1, geog2, s2options)
+}
+
+cpp_s2_union <- function(geog1, geog2, s2options) {
+ .Call(`_s2_cpp_s2_union`, geog1, geog2, s2options)
+}
+
+cpp_s2_difference <- function(geog1, geog2, s2options) {
+ .Call(`_s2_cpp_s2_difference`, geog1, geog2, s2options)
+}
+
+cpp_s2_sym_difference <- function(geog1, geog2, s2options) {
+ .Call(`_s2_cpp_s2_sym_difference`, geog1, geog2, s2options)
+}
+
+cpp_s2_union_agg <- function(geog, s2options, naRm) {
+ .Call(`_s2_cpp_s2_union_agg`, geog, s2options, naRm)
+}
+
+cpp_s2_centroid_agg <- function(geog, naRm) {
+ .Call(`_s2_cpp_s2_centroid_agg`, geog, naRm)
+}
+
+cpp_s2_closest_point <- function(geog1, geog2) {
+ .Call(`_s2_cpp_s2_closest_point`, geog1, geog2)
+}
+
+cpp_s2_minimum_clearance_line_between <- function(geog1, geog2) {
+ .Call(`_s2_cpp_s2_minimum_clearance_line_between`, geog1, geog2)
+}
+
+cpp_s2_centroid <- function(geog) {
+ .Call(`_s2_cpp_s2_centroid`, geog)
+}
+
+cpp_s2_boundary <- function(geog) {
+ .Call(`_s2_cpp_s2_boundary`, geog)
+}
+
+cpp_s2_rebuild <- function(geog, s2options) {
+ .Call(`_s2_cpp_s2_rebuild`, geog, s2options)
+}
+
+cpp_s2_buffer_cells <- function(geog, distance, maxCells, minLevel) {
+ .Call(`_s2_cpp_s2_buffer_cells`, geog, distance, maxCells, minLevel)
+}
+
+s2_xptr_test <- function(size) {
+ .Call(`_s2_s2_xptr_test`, size)
+}
+
+s2_xptr_test_op <- function(s2_xptr_test) {
+ invisible(.Call(`_s2_s2_xptr_test_op`, s2_xptr_test))
+}
+
--- /dev/null
+
+#' Low-resolution world boundaries, timezones, and cities
+#'
+#' Well-known binary versions of the [Natural Earth](https://www.naturalearthdata.com/)
+#' low-resolution world boundaries and timezone boundaries.
+#'
+#' @param name The name of a country, continent, city, or `NULL`
+#' for all features.
+#' @param utc_offset_min,utc_offset_max Minimum and/or maximum timezone
+#' offsets.
+#'
+#' @format A data.frame with columns `name` (character), and
+#' `geometry` (wk_wkb)
+#' @source [Natural Earth Data](https://www.naturalearthdata.com/)
+#' @examples
+#' head(s2_data_countries())
+#' s2_data_countries("Germany")
+#' s2_data_countries("Europe")
+#'
+#' head(s2_data_timezones())
+#' s2_data_timezones(-4)
+#'
+#' head(s2_data_cities())
+#' s2_data_cities("Cairo")
+#'
+"s2_data_tbl_countries"
+
+#' @rdname s2_data_tbl_countries
+"s2_data_tbl_timezones"
+
+#' @rdname s2_data_tbl_countries
+"s2_data_tbl_cities"
+
+#' @rdname s2_data_tbl_countries
+#' @export
+s2_data_countries <- function(name = NULL) {
+ df <- s2::s2_data_tbl_countries
+ if (is.null(name)) {
+ wkb <- df$geometry
+ } else {
+ wkb <- structure(df$geometry[(df$name %in% name) | (df$continent %in% name)], class = "wk_wkb")
+ }
+
+ as_s2_geography(wkb)
+}
+
+#' @rdname s2_data_tbl_countries
+#' @export
+s2_data_timezones <- function(utc_offset_min = NULL, utc_offset_max = utc_offset_min) {
+ df <- s2::s2_data_tbl_timezones
+ if (is.null(utc_offset_min)) {
+ wkb <- df$geometry
+ } else {
+ matches <- (df$utc_offset >= utc_offset_min) & (df$utc_offset <= utc_offset_max)
+ wkb <- structure(df$geometry[matches], class = "wk_wkb")
+ }
+
+ as_s2_geography(wkb)
+}
+
+#' @rdname s2_data_tbl_countries
+#' @export
+s2_data_cities <- function(name = NULL) {
+ df <- s2::s2_data_tbl_cities
+ if (is.null(name)) {
+ wkb <- df$geometry
+ } else {
+ wkb <- structure(df$geometry[(df$name %in% name)], class = "wk_wkb")
+ }
+
+ as_s2_geography(wkb)
+}
--- /dev/null
+
+#' S2 Geography Accessors
+#'
+#' Accessors extract information about [geography vectors][as_s2_geography].
+#'
+#' @param x,y [geography vectors][as_s2_geography]. These inputs
+#' are passed to [as_s2_geography()], so you can pass other objects
+#' (e.g., character vectors of well-known text) directly.
+#' @param radius Radius of the earth. Defaults to the average radius of
+#' the earth in meters as defined by [s2_earth_radius_meters()].
+#'
+#' @export
+#'
+#' @seealso
+#' BigQuery's geography function reference:
+#'
+#' - [ST_ISCOLLECTION](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_iscollection)
+#' - [ST_DIMENSION](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_dimension)
+#' - [ST_NUMPOINTS](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_numpoints)
+#' - [ST_ISEMPTY](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_isempty)
+#' - [ST_AREA](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_area)
+#' - [ST_LENGTH](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_length)
+#' - [ST_PERIMETER](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_perimeter)
+#' - [ST_X](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_x)
+#' - [ST_Y](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_y)
+#' - [ST_DISTANCE](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_distance)
+#' - [ST_MAXDISTANCE](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_maxdistance)
+#'
+#' @examples
+#' # s2_is_collection() tests for multiple geometries in one feature
+#' s2_is_collection(c("POINT (-64 45)", "MULTIPOINT ((-64 45), (8 72))"))
+#'
+#' # s2_dimension() returns 0 for point, 1 for line, 2 for polygon
+#' s2_dimension(
+#' c(
+#' "GEOMETRYCOLLECTION EMPTY",
+#' "POINT (-64 45)",
+#' "LINESTRING (-64 45, 8 72)",
+#' "POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))",
+#' "GEOMETRYCOLLECTION (POINT (-64 45), LINESTRING (-64 45, 8 72))"
+#' )
+#' )
+#'
+#' # s2_num_points() counts points
+#' s2_num_points(c("POINT (-64 45)", "LINESTRING (-64 45, 8 72)"))
+#'
+#' # s2_is_empty tests for emptiness
+#' s2_is_empty(c("POINT (-64 45)", "POINT EMPTY"))
+#'
+#' # calculate area, length, and perimeter
+#' s2_area("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))")
+#' s2_perimeter("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))")
+#' s2_length(s2_boundary("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))"))
+#'
+#' # extract x and y coordinates from points
+#' s2_x(c("POINT (-64 45)", "POINT EMPTY"))
+#' s2_y(c("POINT (-64 45)", "POINT EMPTY"))
+#'
+#' # calculate minimum and maximum distance between two geometries
+#' s2_distance(
+#' "POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))",
+#' "POINT (-64 45)"
+#' )
+#' s2_max_distance(
+#' "POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))",
+#' "POINT (-64 45)"
+#' )
+#'
+s2_is_collection <- function(x) {
+ cpp_s2_is_collection(as_s2_geography(x))
+}
+
+#' @rdname s2_is_collection
+#' @export
+s2_dimension <- function(x) {
+ cpp_s2_dimension(as_s2_geography(x))
+}
+
+#' @rdname s2_is_collection
+#' @export
+s2_num_points <- function(x) {
+ cpp_s2_num_points(as_s2_geography(x))
+}
+
+#' @rdname s2_is_collection
+#' @export
+s2_is_empty <- function(x) {
+ cpp_s2_is_empty(as_s2_geography(x))
+}
+
+#' @rdname s2_is_collection
+#' @export
+s2_area <- function(x, radius = s2_earth_radius_meters()) {
+ recycled <- recycle_common(as_s2_geography(x), radius)
+ cpp_s2_area(recycled[[1]]) * radius ^ 2
+}
+
+#' @rdname s2_is_collection
+#' @export
+s2_length <- function(x, radius = s2_earth_radius_meters()) {
+ recycled <- recycle_common(as_s2_geography(x), radius)
+ cpp_s2_length(recycled[[1]]) * radius
+}
+
+#' @rdname s2_is_collection
+#' @export
+s2_perimeter <- function(x, radius = s2_earth_radius_meters()) {
+ recycled <- recycle_common(as_s2_geography(x), radius)
+ cpp_s2_perimeter(recycled[[1]]) * radius
+}
+
+#' @rdname s2_is_collection
+#' @export
+s2_x <- function(x) {
+ cpp_s2_x(as_s2_geography(x))
+}
+
+#' @rdname s2_is_collection
+#' @export
+s2_y <- function(x) {
+ cpp_s2_y(as_s2_geography(x))
+}
+
+#' @rdname s2_is_collection
+#' @export
+s2_distance <- function(x, y, radius = s2_earth_radius_meters()) {
+ recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y), radius)
+ cpp_s2_distance(recycled[[1]], recycled[[2]]) * radius
+}
+
+#' @rdname s2_is_collection
+#' @export
+s2_max_distance <- function(x, y, radius = s2_earth_radius_meters()) {
+ recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y), radius)
+ cpp_s2_max_distance(recycled[[1]], recycled[[2]]) * radius
+}
--- /dev/null
+
+#' Compute feature-wise and aggregate bounds
+#'
+#' [s2_bounds_rect()] returns a bounding latitude-longitude
+#' rectangle that contains the region; [s2_bounds_cap()] returns a bounding circle
+#' represented by a centre point (lat, lng) and an angle. The bound may not be tight
+#' for points, polylines and geometry collections. The rectangle returned may depend on
+#' the order of points or polylines. `lng_lo` values larger than `lng_hi` indicate
+#' regions that span the antimeridian, see the Fiji example.
+#'
+#' @inheritParams s2_is_collection
+#' @export
+#' @return Both functions return a `data.frame`:
+#'
+#' - [s2_bounds_rect()]: Columns `minlng`, `minlat`, `maxlng`, `maxlat` (degrees)
+#' - [s2_bounds_cap()]: Columns `lng`, `lat`, `angle` (degrees)
+#'
+#' @examples
+#' s2_bounds_cap(s2_data_countries("Antarctica"))
+#' s2_bounds_cap(s2_data_countries("Netherlands"))
+#' s2_bounds_cap(s2_data_countries("Fiji"))
+#'
+#' s2_bounds_rect(s2_data_countries("Antarctica"))
+#' s2_bounds_rect(s2_data_countries("Netherlands"))
+#' s2_bounds_rect(s2_data_countries("Fiji"))
+#'
+s2_bounds_cap <- function(x) {
+ cpp_s2_bounds_cap(as_s2_geography(x))
+}
+
+#' @rdname s2_bounds_cap
+#' @export
+s2_bounds_rect <- function(x) {
+ cpp_s2_bounds_rect(as_s2_geography(x))
+}
--- /dev/null
+
+#' Create and Format Geography Vectors
+#'
+#' These functions create and export [geography vectors][as_s2_geography].
+#' Unlike the BigQuery geography constructors, these functions do not sanitize
+#' invalid or redundant input using [s2_union()]. Note that when creating polygons
+#' using [s2_make_polygon()], rings can be open or closed.
+#'
+#' @inheritParams s2_is_collection
+#' @inheritParams as_s2_geography
+#' @param precision The number of significant digits to export when
+#' writing well-known text. If `trim = FALSE`, the number of
+#' digits after the decimal place.
+#' @param trim Should trailing zeroes be included after the decimal place?
+#' @param endian The endian-ness of the well-known binary. See [wk::wkb_translate_wkb()].
+#' @param longitude,latitude Vectors of latitude and longitude
+#' @param wkt_string Well-known text
+#' @param wkb_bytes A `list()` of `raw()`
+#' @param feature_id,ring_id Vectors for which a change in
+#' sequential values indicates a new feature or ring. Use [factor()]
+#' to convert from a character vector.
+#'
+#' @export
+#'
+#' @seealso
+#' See [as_s2_geography()] for other ways to construct geography vectors.
+#'
+#' BigQuery's geography function reference:
+#'
+#' - [ST_GEOGPOINT](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_geogpoint)
+#' - [ST_MAKELINE](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_makeline)
+#' - [ST_MAKEPOLYGON](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_makepolygon)
+#' - [ST_GEOGFROMTEXT](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_geogfromtext)
+#' - [ST_GEOGFROMWKB](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_geogfromwkb)
+#' - [ST_ASTEXT](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_astext)
+#' - [ST_ASBINARY](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_asbinary)
+#'
+#' @examples
+#' # create point geographies using coordinate values:
+#' s2_geog_point(-64, 45)
+#'
+#' # create line geographies using coordinate values:
+#' s2_make_line(c(-64, 8), c(45, 71))
+#'
+#' # optionally, separate features using feature_id:
+#' s2_make_line(
+#' c(-64, 8, -27, -27), c(45, 71, 0, 45),
+#' feature_id = c(1, 1, 2, 2)
+#' )
+#'
+#' # create polygon geographies using coordinate values:
+#' # (rings can be open or closed)
+#' s2_make_polygon(c(-45, 8, 0), c(64, 71, 90))
+#'
+#' # optionally, separate rings and/or features using
+#' # ring_id and/or feature_id
+#' s2_make_polygon(
+#' c(20, 10, 10, 30, 45, 30, 20, 20, 40, 20, 45),
+#' c(35, 30, 10, 5, 20, 20, 15, 25, 40, 45, 30),
+#' feature_id = c(rep(1, 8), rep(2, 3)),
+#' ring_id = c(1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1)
+#' )
+#'
+#' # import and export well-known text
+#' (geog <- s2_geog_from_text("POINT (-64 45)"))
+#' s2_as_text(geog)
+#'
+#' # import and export well-known binary
+#' (geog <- s2_geog_from_wkb(wk::as_wkb("POINT (-64 45)")))
+#' s2_as_binary(geog)
+#'
+s2_geog_point <- function(longitude, latitude) {
+ recycled <- recycle_common(longitude, latitude)
+ new_s2_xptr(cpp_s2_geog_point(recycled[[1]], recycled[[2]]), "s2_geography")
+}
+
+#' @rdname s2_geog_point
+#' @export
+s2_make_line <- function(longitude, latitude, feature_id = 1L) {
+ recycled <- recycle_common(longitude, latitude, feature_id)
+ new_s2_xptr(cpp_s2_make_line(recycled[[1]], recycled[[2]], featureId = recycled[[3]]), "s2_geography")
+}
+
+#' @rdname s2_geog_point
+#' @export
+s2_make_polygon <- function(longitude, latitude, feature_id = 1L, ring_id = 1L,
+ oriented = FALSE, check = TRUE) {
+ recycled <- recycle_common(longitude, latitude, feature_id, ring_id)
+ new_s2_xptr(
+ cpp_s2_make_polygon(
+ recycled[[1]], recycled[[2]],
+ featureId = recycled[[3]],
+ ringId = recycled[[4]],
+ oriented = oriented,
+ check = check
+ ),
+ "s2_geography"
+ )
+}
+
+#' @rdname s2_geog_point
+#' @export
+s2_geog_from_text <- function(wkt_string, oriented = FALSE, check = TRUE) {
+ wk::validate_wk_wkt(wkt_string)
+ new_s2_xptr(
+ s2_geography_from_wkt(
+ wkt_string,
+ oriented = oriented,
+ check = check
+ ),
+ "s2_geography"
+ )
+}
+
+#' @rdname s2_geog_point
+#' @export
+s2_geog_from_wkb <- function(wkb_bytes, oriented = FALSE, check = TRUE) {
+ wk::validate_wk_wkb(wkb_bytes)
+ new_s2_xptr(
+ s2_geography_from_wkb(
+ wkb_bytes,
+ oriented = oriented,
+ check = check
+ ),
+ "s2_geography"
+ )
+}
+
+#' @rdname s2_geog_point
+#' @export
+s2_as_text <- function(x, precision = 16, trim = TRUE) {
+ s2_geography_to_wkt(as_s2_geography(x), precision = precision, trim = trim)
+}
+
+#' @rdname s2_geog_point
+#' @export
+s2_as_binary <- function(x, endian = wk::wk_platform_endian()) {
+ structure(s2_geography_to_wkb(as_s2_geography(x), endian = endian), class = "blob")
+}
--- /dev/null
+
+#' Earth Constants
+#'
+#' According to Yoder (1995), the radius of the earth is
+#' 6371.01 km. These functions are used to set the
+#' default radis for functions that return a distance
+#' or accept a distance as input
+#' (e.g., [s2_distance()] and [s2_dwithin()]).
+#'
+#' @export
+#'
+#' @references
+#' Yoder, C.F. 1995. "Astrometric and Geodetic Properties of Earth and the
+#' Solar System" in Global Earth Physics, A Handbook of Physical Constants,
+#' AGU Reference Shelf 1, American Geophysical Union, Table 2.
+#' \doi{10.1029/RF001p0001}
+#'
+#' @examples
+#' s2_earth_radius_meters()
+#'
+s2_earth_radius_meters <- function() {
+ 6371.01 * 1000
+}
--- /dev/null
+
+#' Create an S2 Geography Vector
+#'
+#' Geography vectors are arrays of points, lines, polygons, and/or collections
+#' of these. Geography vectors assume coordinates are longitude and latitude
+#' on a perfect sphere.
+#'
+#' The coercion function [as_s2_geography()] is used to wrap the input
+#' of most functions in the s2 package so that you can use other objects with
+#' an unambiguious interpretation as a geography vector. Geography vectors
+#' have a minimal [vctrs][vctrs::vctrs-package] implementation, so you can
+#' use these objects in tibble, dplyr, and other packages that use the vctrs
+#' framework.
+#'
+#' @param x An object that can be converted to an s2_geography vector
+#' @param oriented TRUE if polygon ring directions are known to be correct
+#' (i.e., exterior rings are defined counter clockwise and interior
+#' rings are defined clockwise).
+#' @param check Use `check = FALSE` to skip error on invalid geometries
+#' @param ... Unused
+#'
+#' @return An object with class s2_geography
+#' @export
+#'
+#' @seealso
+#' [s2_geog_from_wkb()], [s2_geog_from_text()], [s2_geog_point()],
+#' [s2_make_line()], [s2_make_polygon()] for other ways to
+#' create geography vectors, and [s2_as_binary()] and [s2_as_text()]
+#' for other ways to export them.
+#'
+as_s2_geography <- function(x, ...) {
+ UseMethod("as_s2_geography")
+}
+
+#' @rdname as_s2_geography
+#' @export
+as_s2_geography.default <- function(x, ...) {
+ as_s2_geography(wk::as_wkb(x))
+}
+
+#' @rdname as_s2_geography
+#' @export
+as_s2_geography.s2_geography <- function(x, ...) {
+ x
+}
+
+#' @rdname as_s2_geography
+#' @export
+as_s2_geography.s2_lnglat <- function(x, ...) {
+ df <- data_frame_from_s2_lnglat(x)
+ new_s2_xptr(cpp_s2_geog_point(df[[1]], df[[2]]), "s2_geography")
+}
+
+#' @rdname as_s2_geography
+#' @export
+as_s2_geography.s2_point <- function(x, ...) {
+ as_s2_geography(as_s2_lnglat(x))
+}
+
+#' @rdname as_s2_geography
+#' @export
+as_s2_geography.wk_wkb <- function(x, ..., oriented = FALSE, check = TRUE) {
+ new_s2_xptr(
+ s2_geography_from_wkb(x, oriented = oriented, check = check),
+ "s2_geography"
+ )
+}
+
+#' @rdname as_s2_geography
+#' @export
+as_s2_geography.WKB <- function(x, ..., oriented = FALSE, check = TRUE) {
+ new_s2_xptr(
+ s2_geography_from_wkb(x, oriented = oriented, check = check),
+ "s2_geography"
+ )
+}
+
+#' @rdname as_s2_geography
+#' @export
+as_s2_geography.blob <- function(x, ..., oriented = FALSE, check = TRUE) {
+ new_s2_xptr(
+ s2_geography_from_wkb(x, oriented = oriented, check = check),
+ "s2_geography"
+ )
+}
+
+#' @rdname as_s2_geography
+#' @export
+as_s2_geography.wk_wkt <- function(x, ..., oriented = FALSE, check = TRUE) {
+ new_s2_xptr(
+ s2_geography_from_wkt(x, oriented = oriented, check = check),
+ "s2_geography"
+ )
+}
+
+#' @rdname as_s2_geography
+#' @export
+as_s2_geography.character <- function(x, ..., oriented = FALSE, check = TRUE) {
+ new_s2_xptr(
+ s2_geography_from_wkt(x, oriented = oriented, check = check),
+ "s2_geography"
+ )
+}
+
+#' @rdname as_s2_geography
+#' @export
+as_s2_geography.logical <- function(x, ...) {
+ stopifnot(isTRUE(x))
+ new_s2_xptr(s2_geography_full(TRUE), "s2_geography")
+}
+
+#' @importFrom wk as_wkb
+#' @rdname as_s2_geography
+#' @export
+as_wkb.s2_geography <- function(x, ...) {
+ wk::new_wk_wkb(s2_geography_to_wkb(x, wk::wk_platform_endian()))
+}
+
+#' @importFrom wk as_wkt
+#' @rdname as_s2_geography
+#' @export
+as_wkt.s2_geography <- function(x, ...) {
+ wk::new_wk_wkt(s2_geography_to_wkt(x, precision = 16, trim = TRUE))
+}
+
+
+#' @export
+`[<-.s2_geography` <- function(x, i, value) {
+ x <- unclass(x)
+ x[i] <- as_s2_geography(value)
+ new_s2_xptr(x, "s2_geography")
+}
+
+#' @export
+`[[<-.s2_geography` <- function(x, i, value) {
+ x <- unclass(x)
+ x[i] <- as_s2_geography(value)
+ new_s2_xptr(x, "s2_geography")
+}
+
+#' @export
+format.s2_geography <- function(x, ..., max_coords = 5, precision = 9, trim = TRUE) {
+ paste0("<", s2_geography_format(x, max_coords, precision, trim), ">")
+}
+
+# this is what gets called by the RStudio viewer, for which
+# format() is best suited (s2_as_text() is more explicit for WKT output)
+#' @export
+as.character.s2_geography <- function(x, ..., max_coords = 5, precision = 9, trim = TRUE) {
+ format(x, ..., max_coords = max_coords, precision = precision, trim = trim)
+}
--- /dev/null
+
+#' Create an S2 LngLat Vector
+#'
+#' This class represents a latitude and longitude on the Earth's surface.
+#' Most calculations in S2 convert this to a [as_s2_point()], which is a
+#' unit vector representation of this value.
+#'
+#' @param lat,lng Vectors of latitude and longitude values in degrees.
+#' @param x A [s2_lnglat()] vector or an object that can be coerced to one.
+#' @param ... Unused
+#'
+#' @return An object with class s2_lnglat
+#' @export
+#'
+#' @examples
+#' s2_lnglat(45, -64) # Halifax, Nova Scotia!
+#' as.data.frame(s2_lnglat(45, -64))
+#'
+s2_lnglat <- function(lng, lat) {
+ recycled <- recycle_common(as.double(lng), as.double(lat))
+ new_s2_xptr(s2_lnglat_from_numeric(recycled[[1]], recycled[[2]]), "s2_lnglat")
+}
+
+#' @rdname s2_lnglat
+#' @export
+as_s2_lnglat <- function(x, ...) {
+ UseMethod("as_s2_lnglat")
+}
+
+#' @rdname s2_lnglat
+#' @export
+as_s2_lnglat.s2_lnglat <- function(x, ...) {
+ x
+}
+
+#' @rdname s2_lnglat
+#' @export
+as_s2_lnglat.s2_point <- function(x, ...) {
+ new_s2_xptr(s2_lnglat_from_s2_point(x), "s2_lnglat")
+}
+
+#' @rdname s2_lnglat
+#' @export
+as_s2_lnglat.s2_geography <- function(x, ...) {
+ new_s2_xptr(s2_lnglat_from_numeric(cpp_s2_x(x), cpp_s2_y(x)), "s2_lnglat")
+}
+
+#' @rdname s2_lnglat
+#' @export
+as_s2_lnglat.matrix <- function(x, ...) {
+ s2_lnglat(x[, 1, drop = TRUE], x[, 2, drop = TRUE])
+}
+
+#' @export
+as_s2_lnglat.character <- function(x, ...) {
+ as_s2_lnglat.wk_wkt(x)
+}
+
+#' @export
+as_s2_lnglat.wk_wkt <- function(x, ...) {
+ as_s2_lnglat(as_s2_geography(x), ...)
+}
+
+#' @export
+as_s2_lnglat.wk_wkb <- function(x, ...) {
+ as_s2_lnglat(as_s2_geography(x), ...)
+}
+
+#' @rdname s2_lnglat
+#' @export
+as.data.frame.s2_lnglat <- function(x, ...) {
+ as.data.frame(data_frame_from_s2_lnglat(x))
+}
+
+#' @rdname s2_lnglat
+#' @export
+as.matrix.s2_lnglat <- function(x, ...) {
+ as.matrix(as.data.frame(data_frame_from_s2_lnglat(x)))
+}
+
+#' @rdname s2_lnglat
+#' @importFrom wk as_wkb
+#' @export
+as_wkb.s2_lnglat <- function(x, ...) {
+ as_wkb(as_s2_geography(x), ...)
+}
+
+#' @rdname s2_lnglat
+#' @importFrom wk as_wkt
+#' @export
+as_wkt.s2_lnglat <- function(x, ...) {
+ as_wkt(as_s2_geography(x), ...)
+}
+
+#' @export
+`[<-.s2_lnglat` <- function(x, i, value) {
+ x <- unclass(x)
+ x[i] <- as_s2_lnglat(value)
+ new_s2_xptr(x, "s2_lnglat")
+}
+
+#' @export
+`[[<-.s2_lnglat` <- function(x, i, value) {
+ x <- unclass(x)
+ x[i] <- as_s2_lnglat(value)
+ new_s2_xptr(x, "s2_lnglat")
+}
+
+#' @export
+format.s2_lnglat <- function(x, ...) {
+ df <- as.data.frame(x)
+ sprintf("(%s, %s)", format(df$lng, trim = TRUE), format(df$lat, trim = TRUE))
+}
--- /dev/null
+
+#' Matrix Functions
+#'
+#' These functions are similar to accessors and predicates, but instead of
+#' recycling `x` and `y` to a common length and returning a vector of that
+#' length, these functions return a vector of length `x` with each element
+#' `i` containing information about how the entire vector `y` relates to
+#' the feature at `x[i]`.
+#'
+#' @inheritParams s2_is_collection
+#' @inheritParams s2_contains
+#' @param x,y Geography vectors, coerced using [as_s2_geography()].
+#' `x` is considered the source, where as `y` is considered the target.
+#' @param max_edges_per_cell For [s2_may_intersect_matrix()],
+#' this values controls the nature of the index on `y`, with higher values
+#' leading to coarser index. Values should be between 10 and 50; the default
+#' of 50 is adequate for most use cases, but for specialized operations users
+#' may wish to use a lower value to increase performance.
+#' @param max_feature_cells For [s2_may_intersect_matrix()], this value
+#' controls the approximation of `x` used to identify potential intersections
+#' on `y`. The default value of 4 gives the best performance for most operations,
+#' but for specialized operations users may wish to use a higher value to increase
+#' performance.
+#'
+#' @return A vector of length `x`.
+#' @export
+#'
+#' @seealso
+#' See pairwise predicate functions (e.g., [s2_intersects()]).
+#'
+#' @examples
+#' city_names <- c("Vatican City", "San Marino", "Luxembourg")
+#' cities <- s2_data_cities(city_names)
+#' country_names <- s2_data_tbl_countries$name
+#' countries <- s2_data_countries()
+#'
+#' # closest feature returns y indices of the closest feature
+#' # for each feature in x
+#' country_names[s2_closest_feature(cities, countries)]
+#'
+#' # farthest feature returns y indices of the farthest feature
+#' # for each feature in x
+#' country_names[s2_farthest_feature(cities, countries)]
+#'
+#' # predicate matrices
+#' country_names[s2_intersects_matrix(cities, countries)[[1]]]
+#'
+#' # distance matrices
+#' s2_distance_matrix(cities, cities)
+#' s2_max_distance_matrix(cities, countries[1:4])
+#'
+s2_closest_feature <- function(x, y) {
+ cpp_s2_closest_feature(as_s2_geography(x), as_s2_geography(y))
+}
+
+#' @rdname s2_closest_feature
+#' @export
+s2_farthest_feature <- function(x, y) {
+ cpp_s2_farthest_feature(as_s2_geography(x), as_s2_geography(y))
+}
+
+#' @rdname s2_closest_feature
+#' @export
+s2_distance_matrix <- function(x, y, radius = s2_earth_radius_meters()) {
+ cpp_s2_distance_matrix(as_s2_geography(x), as_s2_geography(y)) * radius
+}
+
+#' @rdname s2_closest_feature
+#' @export
+s2_max_distance_matrix <- function(x, y, radius = s2_earth_radius_meters()) {
+ cpp_s2_max_distance_matrix(as_s2_geography(x), as_s2_geography(y)) * radius
+}
+
+#' @rdname s2_closest_feature
+#' @export
+s2_contains_matrix <- function(x, y, options = s2_options(model = "open")) {
+ cpp_s2_contains_matrix(as_s2_geography(x), as_s2_geography(y), options)
+}
+
+#' @rdname s2_closest_feature
+#' @export
+s2_within_matrix <- function(x, y, options = s2_options(model = "open")) {
+ cpp_s2_within_matrix(as_s2_geography(x), as_s2_geography(y), options)
+}
+
+#' @rdname s2_closest_feature
+#' @export
+s2_covers_matrix <- function(x, y, options = s2_options(model = "closed")) {
+ cpp_s2_contains_matrix(as_s2_geography(x), as_s2_geography(y), options)
+}
+
+#' @rdname s2_closest_feature
+#' @export
+s2_covered_by_matrix <- function(x, y, options = s2_options(model = "closed")) {
+ cpp_s2_within_matrix(as_s2_geography(x), as_s2_geography(y), options)
+}
+
+#' @rdname s2_closest_feature
+#' @export
+s2_intersects_matrix <- function(x, y, options = s2_options()) {
+ cpp_s2_intersects_matrix(as_s2_geography(x), as_s2_geography(y), options)
+}
+
+#' @rdname s2_closest_feature
+#' @export
+s2_disjoint_matrix <- function(x, y, options = s2_options()) {
+ # disjoint is the odd one out, in that it requires a negation of intersects
+ # this is inconvenient to do on the C++ level, and is easier to maintain
+ # with setdiff() here (unless somebody complains that this is slow)
+ intersection <- cpp_s2_intersects_matrix(as_s2_geography(x), as_s2_geography(y), options)
+ Map(setdiff, list(seq_along(y)), intersection)
+}
+
+#' @rdname s2_closest_feature
+#' @export
+s2_equals_matrix <- function(x, y, options = s2_options()) {
+ cpp_s2_equals_matrix(as_s2_geography(x), as_s2_geography(y), options)
+}
+
+#' @rdname s2_closest_feature
+#' @export
+s2_touches_matrix <- function(x, y, options = s2_options()) {
+ cpp_s2_touches_matrix(as_s2_geography(x), as_s2_geography(y), options)
+}
+
+#' @rdname s2_closest_feature
+#' @export
+s2_dwithin_matrix <- function(x, y, distance, radius = s2_earth_radius_meters()) {
+ cpp_s2_dwithin_matrix(as_s2_geography(x), as_s2_geography(y), distance / radius)
+}
+
+#' @rdname s2_closest_feature
+#' @export
+s2_may_intersect_matrix <- function(x, y, max_edges_per_cell = 50, max_feature_cells = 4) {
+ cpp_s2_may_intersect_matrix(
+ as_s2_geography(x), as_s2_geography(y),
+ max_edges_per_cell, max_feature_cells,
+ s2_options()
+ )
+}
+
+# ------- for testing, non-indexed versions of matrix operators -------
+
+s2_contains_matrix_brute_force <- function(x, y, options = s2_options()) {
+ cpp_s2_contains_matrix_brute_force(as_s2_geography(x), as_s2_geography(y), options)
+}
+
+s2_within_matrix_brute_force <- function(x, y, options = s2_options()) {
+ cpp_s2_within_matrix_brute_force(as_s2_geography(x), as_s2_geography(y), options)
+}
+
+s2_covers_matrix_brute_force <- function(x, y, options = s2_options(model = "closed")) {
+ cpp_s2_contains_matrix_brute_force(as_s2_geography(x), as_s2_geography(y), options)
+}
+
+s2_covered_by_matrix_brute_force <- function(x, y, options = s2_options(model = "closed")) {
+ cpp_s2_within_matrix_brute_force(as_s2_geography(x), as_s2_geography(y), options)
+}
+
+s2_intersects_matrix_brute_force <- function(x, y, options = s2_options()) {
+ cpp_s2_intersects_matrix_brute_force(as_s2_geography(x), as_s2_geography(y), options)
+}
+
+s2_disjoint_matrix_brute_force <- function(x, y, options = s2_options()) {
+ cpp_s2_disjoint_matrix_brute_force(as_s2_geography(x), as_s2_geography(y), options)
+}
+
+s2_equals_matrix_brute_force <- function(x, y, options = s2_options()) {
+ cpp_s2_equals_matrix_brute_force(as_s2_geography(x), as_s2_geography(y), options)
+}
--- /dev/null
+
+#' Geography Operation Options
+#'
+#' These functions specify defaults for options used to perform operations
+#' and construct geometries. These are used in predicates (e.g., [s2_intersects()]),
+#' and boolean operations (e.g., [s2_intersection()]) to specify the model for
+#' containment and how new geometries should be constructed.
+#'
+#' @param model One of 'open', 'semi-open' (default for polygons),
+#' or 'closed' (default for polylines). See section 'Model'
+#' @param snap Use `s2_snap_identity()`, `s2_snap_distance()`, `s2_snap_level()`,
+#' or `s2_snap_precision()` to specify how or if coordinate rounding should
+#' occur.
+#' @param snap_radius As opposed to the snap function, which specifies
+#' the maximum distance a vertex should move, the snap radius (in radians) sets
+#' the minimum distance between vertices of the output that don't cause vertices
+#' to move more than the distance specified by the snap function. This can be used
+#' to simplify the result of a boolean operation. Use -1 to specify that any
+#' minimum distance is acceptable.
+#' @param duplicate_edges Use `TRUE` to keep duplicate edges (e.g., duplicate
+#' points).
+#' @param edge_type One of 'directed' (default) or 'undirected'.
+#' @param polyline_type One of 'path' (default) or 'walk'. If 'walk',
+#' polylines that backtrack are preserved.
+#' @param polyline_sibling_pairs One of 'discard' (default) or 'keep'.
+#' @param simplify_edge_chains Use `TRUE` to remove vertices that are within
+#' `snap_radius` of the original vertex.
+#' @param split_crossing_edges Use `TRUE` to split crossing polyline edges
+#' when creating geometries.
+#' @param idempotent Use `FALSE` to apply snap even if snapping is not necessary
+#' to satisfy vertex constraints.
+#' @param validate Use `TRUE` to validate the result from the builder.
+#' @param level A value from 0 to 30 corresponding to the cell level
+#' at which snapping should occur.
+#' @param distance A distance (in radians) denoting the maximum
+#' distance a vertex should move in the snapping process.
+#' @param precision A number by which coordinates should be multiplied
+#' before being rounded. Rounded to the nearest exponent of 10.
+#'
+#' @section Model:
+#' The geometry model indicates whether or not a geometry includes its boundaries.
+#' Boundaries of line geometries are its end points.
+#' OPEN geometries do not contain their boundary (`model = "open"`); CLOSED
+#' geometries (`model = "closed"`) contain their boundary; SEMI-OPEN geometries
+#' (`model = "semi-open"`) contain half of their boundaries, such that when two polygons
+#' do not overlap or two lines do not cross, no point exist that belong to
+#' more than one of the geometries. (This latter form, half-closed, is
+#' not present in the OpenGIS "simple feature access" (SFA) standard nor DE9-IM on
+#' which that is based). The default values for [s2_contains()] (open)
+#' and covers/covered_by (closed) correspond to the SFA standard specification
+#' of these operators.
+#'
+#' @export
+#'
+#' @examples
+#' # use s2_options() to specify containment models, snap level
+#' # layer creation options, and builder options
+#' s2_options(model = "closed", snap = s2_snap_level(30))
+#'
+s2_options <- function(model = NULL,
+ snap = s2_snap_identity(),
+ snap_radius = -1,
+ duplicate_edges = FALSE,
+ edge_type = "directed",
+ validate = FALSE,
+ polyline_type = "path",
+ polyline_sibling_pairs = "keep",
+ simplify_edge_chains = FALSE,
+ split_crossing_edges = FALSE,
+ idempotent = FALSE) {
+ # check snap radius (passing in a huge snap radius can cause problems)
+ if (snap_radius > 3) {
+ stop(
+ "Snap radius is too large. Did you pass in a snap radius in meters instead of radians?",
+ call. = FALSE
+ )
+ }
+
+ structure(
+ list(
+ # model needs to be "unset" by default because there are differences in polygon
+ # and polyline handling by default that are good defaults to preserve
+ model = if (is.null(model)) -1 else match_option(model, c("open", "semi-open", "closed"), "model"),
+ snap = snap,
+ snap_radius = snap_radius,
+ duplicate_edges = duplicate_edges,
+ edge_type = match_option(edge_type, c("directed", "undirected"), "edge_type"),
+ validate = validate,
+ polyline_type = match_option(polyline_type, c("path", "walk"), "polyline_type"),
+ polyline_sibling_pairs = match_option(
+ polyline_sibling_pairs,
+ c("discard", "keep"),
+ "polyline_sibling_pairs"
+ ),
+ simplify_edge_chains = simplify_edge_chains,
+ split_crossing_edges = split_crossing_edges,
+ idempotent = idempotent
+ ),
+ class = "s2_options"
+ )
+}
+
+#' @rdname s2_options
+#' @export
+s2_snap_identity <- function() {
+ structure(list(), class = "snap_identity")
+}
+
+#' @rdname s2_options
+#' @export
+s2_snap_level <- function(level) {
+ if (level > 30) {
+ stop("`level` must be an intger between 1 and 30", call. = FALSE)
+ }
+
+ structure(list(level = level), class = "snap_level")
+}
+
+#' @rdname s2_options
+#' @export
+s2_snap_precision <- function(precision) {
+ structure(list(exponent = round(log10(precision))), class = "snap_precision")
+}
+
+#' @rdname s2_options
+#' @export
+s2_snap_distance <- function(distance) {
+ structure(list(distance = distance), class = "snap_distance")
+}
+
+
+match_option <- function(x, options, arg) {
+ result <- match(x, options)
+ if (identical(result, NA_integer_)) {
+ stop(
+ sprintf("`%s` must be one of %s", arg, paste0('"', options, '"', collapse = ", ")),
+ call. = FALSE
+ )
+ }
+
+ result
+}
--- /dev/null
+#' @keywords internal
+"_PACKAGE"
+
+# The following block is used by usethis to automatically manage
+# roxygen namespace tags. Modify with care!
+## usethis namespace: start
+#' @useDynLib s2, .registration = TRUE
+#' @importFrom Rcpp sourceCpp
+## usethis namespace: end
+NULL
--- /dev/null
+
+#' Create an S2 Point Vector
+#'
+#' In S2 terminology, a "point" is a 3-dimensional unit vector representation
+#' of an [s2_lnglat()]. Internally, all s2 objects are stored as
+#' 3-dimensional unit vectors.
+#'
+#' @param x,y,z Vectors of latitude and longitude values in degrees.
+#' @param ... Unused
+#'
+#' @return An object with class s2_point
+#' @export
+#'
+#' @examples
+#' lnglat <- s2_lnglat(-64, 45) # Halifax, Nova Scotia!
+#' as_s2_point(lnglat)
+#' as.data.frame(as_s2_point(lnglat))
+#'
+s2_point <- function(x, y, z) {
+ recycled <- recycle_common(as.double(x), as.double(y), as.double(z))
+ new_s2_xptr(s2_point_from_numeric(recycled[[1]], recycled[[2]], recycled[[3]]), "s2_point")
+}
+
+#' @rdname s2_point
+#' @export
+as_s2_point <- function(x, ...) {
+ UseMethod("as_s2_point")
+}
+
+#' @rdname s2_point
+#' @export
+as_s2_point.s2_point <- function(x, ...) {
+ x
+}
+
+#' @rdname s2_point
+#' @export
+as_s2_point.s2_lnglat <- function(x, ...) {
+ new_s2_xptr(s2_point_from_s2_lnglat(x), "s2_point")
+}
+
+#' @rdname s2_point
+#' @export
+as_s2_point.s2_geography <- function(x, ...) {
+ as_s2_point(as_s2_lnglat(x))
+}
+
+#' @rdname s2_point
+#' @export
+as_s2_point.matrix <- function(x, ...) {
+ s2_point(x[, 1, drop = TRUE], x[, 2, drop = TRUE], x[, 3, drop = TRUE])
+}
+
+#' @rdname s2_point
+#' @export
+as.data.frame.s2_point <- function(x, ...) {
+ as.data.frame(data_frame_from_s2_point(x))
+}
+
+#' @rdname s2_point
+#' @export
+as.matrix.s2_point <- function(x, ...) {
+ as.matrix(as.data.frame(data_frame_from_s2_point(x)))
+}
+
+#' @export
+`[<-.s2_point` <- function(x, i, value) {
+ x <- unclass(x)
+ x[i] <- as_s2_point(value)
+ new_s2_xptr(x, "s2_point")
+}
+
+#' @export
+`[[<-.s2_point` <- function(x, i, value) {
+ x <- unclass(x)
+ x[i] <- as_s2_point(value)
+ new_s2_xptr(x, "s2_point")
+}
+
+#' @export
+format.s2_point <- function(x, ...) {
+ df <- as.data.frame(x)
+ sprintf(
+ "[%s %s %s]",
+ format(df$x, trim = TRUE),
+ format(df$y, trim = TRUE),
+ format(df$z, trim = TRUE)
+ )
+}
--- /dev/null
+
+#' S2 Geography Predicates
+#'
+#' These functions operate two geography vectors (pairwise), and return
+#' a logical vector.
+#'
+#' @inheritParams s2_is_collection
+#' @inheritParams s2_boundary
+#' @param distance A distance on the surface of the earth in the same units
+#' as `radius`.
+#' @param lng1,lat1,lng2,lat2 A latitude/longitude range
+#' @param detail The number of points with which to approximate
+#' non-geodesic edges.
+#'
+#' @inheritSection s2_options Model
+#'
+#' @export
+#'
+#' @seealso
+#' Matrix versions of these predicates (e.g., [s2_intersects_matrix()]).
+#'
+#' BigQuery's geography function reference:
+#'
+#' - [ST_CONTAINS](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_contains)
+#' - [ST_COVEREDBY](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_coveredby)
+#' - [ST_COVERS](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_covers)
+#' - [ST_DISJOINT](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_disjoint)
+#' - [ST_EQUALS](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_equals)
+#' - [ST_INTERSECTS](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_intersects)
+#' - [ST_INTERSECTSBOX](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_intersectsbox)
+#' - [ST_TOUCHES](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_touches)
+#' - [ST_WITHIN](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_within)
+#' - [ST_DWITHIN](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_dwithin)
+#'
+#' @examples
+#' s2_contains(
+#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+#' c("POINT (5 5)", "POINT (-1 1)")
+#' )
+#'
+#' s2_within(
+#' c("POINT (5 5)", "POINT (-1 1)"),
+#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))"
+#' )
+#'
+#' s2_covered_by(
+#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+#' c("POINT (5 5)", "POINT (-1 1)")
+#' )
+#'
+#' s2_covers(
+#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+#' c("POINT (5 5)", "POINT (-1 1)")
+#' )
+#'
+#' s2_disjoint(
+#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+#' c("POINT (5 5)", "POINT (-1 1)")
+#' )
+#'
+#' s2_intersects(
+#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+#' c("POINT (5 5)", "POINT (-1 1)")
+#' )
+#'
+#' s2_equals(
+#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+#' c(
+#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+#' "POLYGON ((10 0, 10 10, 0 10, 0 0, 10 0))",
+#' "POLYGON ((-1 -1, 10 0, 10 10, 0 10, -1 -1))"
+#' )
+#' )
+#'
+#' s2_intersects(
+#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+#' c("POINT (5 5)", "POINT (-1 1)")
+#' )
+#'
+#' s2_intersects_box(
+#' c("POINT (5 5)", "POINT (-1 1)"),
+#' 0, 0, 10, 10
+#' )
+#'
+#' s2_touches(
+#' "POLYGON ((0 0, 0 1, 1 1, 0 0))",
+#' c("POINT (0 0)", "POINT (0.5 0.75)", "POINT (0 0.5)")
+#' )
+#'
+#' s2_dwithin(
+#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+#' c("POINT (5 5)", "POINT (-1 1)"),
+#' 0 # distance in meters
+#' )
+#'
+#' s2_dwithin(
+#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+#' c("POINT (5 5)", "POINT (-1 1)"),
+#' 1e6 # distance in meters
+#' )
+#'
+s2_contains <- function(x, y, options = s2_options(model = "open")) {
+ recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y))
+ cpp_s2_contains(recycled[[1]], recycled[[2]], options)
+}
+
+#' @rdname s2_contains
+#' @export
+s2_within <- function(x, y, options = s2_options(model = "open")) {
+ s2_contains(y, x, options)
+}
+
+#' @rdname s2_contains
+#' @export
+s2_covered_by <- function(x, y, options = s2_options(model = "closed")) {
+ s2_covers(y, x, options)
+}
+
+#' @rdname s2_contains
+#' @export
+s2_covers <- function(x, y, options = s2_options(model = "closed")) {
+ recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y))
+ cpp_s2_contains(recycled[[1]], recycled[[2]], options)
+}
+
+#' @rdname s2_contains
+#' @export
+s2_disjoint <- function(x, y, options = s2_options()) {
+ !s2_intersects(x, y, options)
+}
+
+#' @rdname s2_contains
+#' @export
+s2_intersects <- function(x, y, options = s2_options()) {
+ recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y))
+ cpp_s2_intersects(recycled[[1]], recycled[[2]], options)
+}
+
+#' @rdname s2_contains
+#' @export
+s2_equals <- function(x, y, options = s2_options()) {
+ recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y))
+ cpp_s2_equals(recycled[[1]], recycled[[2]], options)
+}
+
+#' @rdname s2_contains
+#' @export
+s2_intersects_box <- function(x, lng1, lat1, lng2, lat2, detail = 1000, options = s2_options()) {
+ recycled <- recycle_common(as_s2_geography(x), lng1, lat1, lng2, lat2, detail)
+ cpp_s2_intersects_box(
+ recycled[[1]],
+ recycled[[2]], recycled[[3]],
+ recycled[[4]], recycled[[5]],
+ detail = recycled[[6]],
+ s2options = options
+ )
+}
+
+#' @rdname s2_contains
+#' @export
+s2_touches <- function(x, y, options = s2_options()) {
+ recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y))
+ cpp_s2_touches(recycled[[1]], recycled[[2]], options)
+}
+
+#' @rdname s2_contains
+#' @export
+s2_dwithin <- function(x, y, distance, radius = s2_earth_radius_meters()) {
+ recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y), distance / radius)
+ cpp_s2_dwithin(recycled[[1]], recycled[[2]], recycled[[3]])
+}
--- /dev/null
+
+#' S2 Geography Transformations
+#'
+#' These functions operate on one or more geography vectors and
+#' return a geography vector.
+#'
+#' @inheritParams s2_is_collection
+#' @param na.rm For aggregate calculations use `na.rm = TRUE`
+#' to drop missing values.
+#' @param grid_size The grid size to which coordinates should be snapped;
+#' will be rounded to the nearest power of 10.
+#' @param options An [s2_options()] object describing the polygon/polyline
+#' model to use and the snap level.
+#' @param distance The distance to buffer, in units of `radius`.
+#' @param max_cells The maximum number of cells to approximate a buffer.
+#' @param min_level The minimum cell level used to approximate a buffer
+#' (1 - 30). Setting this value too high will result in unnecessarily
+#' large geographies, but may help improve buffers along long, narrow
+#' regions.
+#' @param tolerance The minimum distance between vertexes to use when
+#' simplifying a geography.
+#'
+#' @inheritSection s2_options Model
+#'
+#' @export
+#'
+#' @seealso
+#' BigQuery's geography function reference:
+#'
+#' - [ST_BOUNDARY](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_boundary)
+#' - [ST_CENTROID](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_centroid)
+#' - [ST_CLOSESTPOINT](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_closestpoint)
+#' - [ST_DIFFERENCE](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_difference)
+#' - [ST_INTERSECTION](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_intersection)
+#' - [ST_UNION](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_union)
+#' - [ST_SNAPTOGRID](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_snaptogrid)
+#' - [ST_SIMPLIFY](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_simplify)
+#' - [ST_UNION_AGG](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_union_agg)
+#' - [ST_CENTROID_AGG](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#s2_centroid_agg)
+#'
+#' @examples
+#' # returns the boundary:
+#' # empty for point, endpoints of a linestring,
+#' # perimeter of a polygon
+#' s2_boundary("POINT (-64 45)")
+#' s2_boundary("LINESTRING (0 0, 10 0)")
+#' s2_boundary("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))")
+#'
+#' # returns the area-weighted centroid, element-wise
+#' s2_centroid("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))")
+#' s2_centroid("LINESTRING (0 0, 10 0)")
+#'
+#' # returns the unweighted centroid of the entire input
+#' s2_centroid_agg(c("POINT (0 0)", "POINT (10 0)"))
+#'
+#' # returns the closest point on x to y
+#' s2_closest_point(
+#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+#' "POINT (0 90)" # north pole!
+#' )
+#'
+#' # returns the shortest possible line between x and y
+#' s2_minimum_clearance_line_between(
+#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+#' "POINT (0 90)" # north pole!
+#' )
+#'
+#' # binary operations: difference, symmetric difference, intersection and union
+#' s2_difference(
+#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+#' "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))",
+#' # 32 bit platforms may need to set snap rounding
+#' s2_options(snap = s2_snap_level(30))
+#' )
+#'
+#' s2_sym_difference(
+#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+#' "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))",
+#' # 32 bit platforms may need to set snap rounding
+#' s2_options(snap = s2_snap_level(30))
+#' )
+#'
+#' s2_intersection(
+#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+#' "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))",
+#' # 32 bit platforms may need to set snap rounding
+#' s2_options(snap = s2_snap_level(30))
+#' )
+#'
+#' s2_union(
+#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+#' "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))",
+#' # 32 bit platforms may need to set snap rounding
+#' s2_options(snap = s2_snap_level(30))
+#' )
+#'
+#' # use s2_union_agg() to aggregate geographies in a vector
+#' s2_union_agg(
+#' c(
+#' "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+#' "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))"
+#' ),
+#' # 32 bit platforms may need to set snap rounding
+#' s2_options(snap = s2_snap_level(30))
+#' )
+#'
+#' # snap to grid rounds coordinates to a specified grid size
+#' s2_snap_to_grid("POINT (0.333333333333 0.666666666666)", 1e-2)
+#'
+s2_boundary <- function(x) {
+ new_s2_xptr(cpp_s2_boundary(as_s2_geography(x)), "s2_geography")
+}
+
+#' @rdname s2_boundary
+#' @export
+s2_centroid <- function(x) {
+ new_s2_xptr(cpp_s2_centroid(as_s2_geography(x)), "s2_geography")
+}
+
+#' @rdname s2_boundary
+#' @export
+s2_closest_point <- function(x, y) {
+ recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y))
+ new_s2_xptr(cpp_s2_closest_point(recycled[[1]], recycled[[2]]), "s2_geography")
+}
+
+#' @rdname s2_boundary
+#' @export
+s2_minimum_clearance_line_between <- function(x, y) {
+ recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y))
+ new_s2_xptr(cpp_s2_minimum_clearance_line_between(recycled[[1]], recycled[[2]]), "s2_geography")
+}
+
+#' @rdname s2_boundary
+#' @export
+s2_difference <- function(x, y, options = s2_options()) {
+ recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y))
+ new_s2_xptr(cpp_s2_difference(recycled[[1]], recycled[[2]], options), "s2_geography")
+}
+
+#' @rdname s2_boundary
+#' @export
+s2_sym_difference <- function(x, y, options = s2_options()) {
+ recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y))
+ new_s2_xptr(cpp_s2_sym_difference(recycled[[1]], recycled[[2]], options), "s2_geography")
+}
+
+#' @rdname s2_boundary
+#' @export
+s2_intersection <- function(x, y, options = s2_options()) {
+ recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y))
+ new_s2_xptr(cpp_s2_intersection(recycled[[1]], recycled[[2]], options), "s2_geography")
+}
+
+#' @rdname s2_boundary
+#' @export
+s2_union <- function(x, y = NULL, options = s2_options()) {
+ if (is.null(y)) {
+ y <- as_s2_geography("POINT EMPTY")
+ }
+
+ recycled <- recycle_common(as_s2_geography(x), as_s2_geography(y))
+ new_s2_xptr(cpp_s2_union(recycled[[1]], recycled[[2]], options), "s2_geography")
+}
+
+#' @rdname s2_boundary
+#' @export
+s2_snap_to_grid <- function(x, grid_size) {
+ s2_rebuild(
+ x,
+ options = s2_options(
+ snap = s2_snap_precision(10^(-log10(grid_size))),
+ duplicate_edges = TRUE
+ )
+ )
+}
+
+#' @rdname s2_boundary
+#' @export
+s2_simplify <- function(x, tolerance, radius = s2_earth_radius_meters()) {
+ s2_rebuild(x, options = s2_options(snap_radius = tolerance / radius, simplify_edge_chains = TRUE))
+}
+
+#' @rdname s2_boundary
+#' @export
+s2_rebuild <- function(x, options = s2_options()) {
+ new_s2_xptr(cpp_s2_rebuild(as_s2_geography(x), options), "s2_geography")
+}
+
+#' @rdname s2_boundary
+#' @export
+s2_buffer_cells <- function(x, distance, max_cells = 1000, min_level = -1,
+ radius = s2_earth_radius_meters()) {
+ recycled <- recycle_common(as_s2_geography(x), distance / radius)
+ new_s2_xptr(cpp_s2_buffer_cells(recycled[[1]], recycled[[2]], max_cells, min_level), "s2_geography")
+}
+
+#' @rdname s2_boundary
+#' @export
+s2_centroid_agg <- function(x, na.rm = FALSE) {
+ new_s2_xptr(cpp_s2_centroid_agg(as_s2_geography(x), naRm = na.rm), "s2_geography")
+}
+
+#' @rdname s2_boundary
+#' @export
+s2_union_agg <- function(x, options = s2_options(), na.rm = FALSE) {
+ new_s2_xptr(cpp_s2_union_agg(as_s2_geography(x), options, na.rm), "s2_geography")
+}
--- /dev/null
+
+#' Create vectors of XPtr objects
+#'
+#' @param x A bare `list()` of external pointers
+#' @param class A character vector subclass
+#' @param ... Unused
+#'
+#' @return An object of class s2_xptr
+#' @noRd
+#'
+new_s2_xptr <- function(x = list(), class = character()) {
+ if (!is.list(x) || is.object(x)) {
+ stop("x must be a bare list of 'externalptr' objects")
+ }
+
+ class(x) <- union(class, "s2_xptr")
+ x
+}
+
+validate_s2_xptr <- function(x) {
+ type <- vapply(unclass(x), typeof, character(1))
+ valid_items <- type %in% c("externalptr", "NULL")
+ if (any(!valid_items)) {
+ stop("Items must be externalptr objects or NULL")
+ }
+
+ invisible(x)
+}
+
+#' @export
+`[.s2_xptr` <- function(x, i) {
+ new_s2_xptr(NextMethod(), class(x))
+}
+
+# makes lapply() along these vectors possible
+#' @export
+`[[.s2_xptr` <- function(x, i) {
+ x[i]
+}
+
+#' @export
+`c.s2_xptr` <- function(...) {
+ # make sure all items inherit the same top-level class
+ dots <- list(...)
+ inherits_first <- vapply(dots, inherits, class(dots[[1]])[1], FUN.VALUE = logical(1))
+ if (!all(inherits_first)) {
+ stop(sprintf("All items must inherit from '%s'", class(dots[[1]])[1]))
+ }
+
+ xptr <- new_s2_xptr(NextMethod(), class(dots[[1]]))
+ validate_s2_xptr(xptr)
+ xptr
+}
+
+#' @export
+rep.s2_xptr <- function(x, ...) {
+ if (length(x) == 0) {
+ new_s2_xptr(list(), class(x))
+ } else {
+ new_s2_xptr(NextMethod(), class(x))
+ }
+}
+
+#' @method rep_len s2_xptr
+#' @export
+rep_len.s2_xptr <- function(x, length.out) {
+ rep(x, length.out = length.out)
+}
+
+#' @export
+print.s2_xptr <- function(x, ...) {
+ cat(sprintf("<%s[%s]>\n", class(x)[1], length(x)))
+ if (length(x) == 0) {
+ return(invisible(x))
+ }
+
+ out <- stats::setNames(format(x, ...), names(x))
+ print(out, quote = FALSE)
+ invisible(x)
+}
--- /dev/null
+
+recycle_common <- function(...) {
+ dots <- list(...)
+ lengths <- vapply(dots, length, integer(1))
+ non_constant_lengths <- unique(lengths[lengths != 1])
+ if (length(non_constant_lengths) == 0) {
+ final_length <- 1
+ } else if(length(non_constant_lengths) == 1) {
+ final_length <- non_constant_lengths
+ } else {
+ lengths_label <- paste0(non_constant_lengths, collapse = ", ")
+ stop(sprintf("Incompatible lengths: %s", lengths_label))
+ }
+
+ lapply(dots, rep_len, final_length)
+}
+
+expect_wkt_equal <- function(x, y, precision = 16) {
+ testthat::expect_equal(
+ s2_geography_to_wkt(as_s2_geography(x), precision = precision, trim = TRUE),
+ s2_geography_to_wkt(as_s2_geography(y), precision = precision, trim = TRUE)
+ )
+}
+
+expect_near <- function(x, y, epsilon) {
+ testthat::expect_true(abs(y - x) < epsilon)
+}
--- /dev/null
+
+vec_proxy.s2_geography <- function(x, ...) {
+ unclass(x)
+}
+
+vec_restore.s2_geography <- function(x, ...) {
+ new_s2_xptr(x, "s2_geography")
+}
+
+vec_ptype_abbr.s2_geography <- function(x, ...) {
+ "s2_geography"
+}
+
+vec_proxy.s2_point <- function(x, ...) {
+ unclass(x)
+}
+
+vec_restore.s2_point <- function(x, ...) {
+ new_s2_xptr(x, "s2_point")
+}
+
+vec_ptype_abbr.s2_point <- function(x, ...) {
+ "s2_point"
+}
+
+vec_proxy.s2_lnglat <- function(x, ...) {
+ unclass(x)
+}
+
+vec_restore.s2_lnglat <- function(x, ...) {
+ new_s2_xptr(x, "s2_lnglat")
+}
+
+vec_ptype_abbr.s2_lnglat <- function(x, ...) {
+ "s2_lnglat"
+}
--- /dev/null
+
+# nocov start
+.onLoad <- function(...) {
+ # call c++ init
+ cpp_s2_init()
+
+ # dynamically register vctrs dependencies
+ for (cls in c("s2_geography", "s2_point", "s2_lnglat")) {
+ s3_register("vctrs::vec_proxy", cls)
+ s3_register("vctrs::vec_restore", cls)
+ s3_register("vctrs::vec_ptype_abbr", cls)
+ }
+}
+
+s3_register <- function(generic, class, method = NULL) {
+ stopifnot(is.character(generic), length(generic) == 1)
+ stopifnot(is.character(class), length(class) == 1)
+
+ pieces <- strsplit(generic, "::")[[1]]
+ stopifnot(length(pieces) == 2)
+ package <- pieces[[1]]
+ generic <- pieces[[2]]
+
+ caller <- parent.frame()
+
+ get_method_env <- function() {
+ top <- topenv(caller)
+ if (isNamespace(top)) {
+ asNamespace(environmentName(top))
+ } else {
+ caller
+ }
+ }
+ get_method <- function(method, env) {
+ if (is.null(method)) {
+ get(paste0(generic, ".", class), envir = get_method_env())
+ } else {
+ method
+ }
+ }
+
+ method_fn <- get_method(method)
+ stopifnot(is.function(method_fn))
+
+ # Always register hook in case package is later unloaded & reloaded
+ setHook(
+ packageEvent(package, "onLoad"),
+ function(...) {
+ ns <- asNamespace(package)
+
+ # Refresh the method, it might have been updated by `devtools::load_all()`
+ method_fn <- get_method(method)
+
+ registerS3method(generic, class, method_fn, envir = ns)
+ }
+ )
+
+ # Avoid registration failures during loading (pkgload or regular)
+ if (!isNamespaceLoaded(package)) {
+ return(invisible())
+ }
+
+ envir <- asNamespace(package)
+
+ # Only register if generic can be accessed
+ if (exists(generic, envir)) {
+ registerS3method(generic, class, method_fn, envir = envir)
+ }
+
+ invisible()
+}
+# nocov end
--- /dev/null
+
+<!-- README.md is generated from README.Rmd. Please edit that file -->
+
+# s2
+
+<!-- badges: start -->
+
+[](https://www.tidyverse.org/lifecycle/#experimental)
+
+[](https://codecov.io/gh/r-spatial/s2)
+[](https://cran.r-project.org/package=s2)
+<!-- badges: end -->
+
+The goal of s2 is to provide R bindings to Google’s
+[S2Geometry](https://s2geometry.io) library. The package exposes an API
+similar to Google’s [BigQuery Geography
+API](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions),
+whose functions also operate on spherical geometries. This package is a
+complete rewrite of an earlier CRAN package s2 with versions up to
+0.4-2, for which the sources are found
+[here](https://github.com/spatstat/s2/).
+
+## Installation
+
+You can install the released version of s2 from
+[CRAN](https://CRAN.R-project.org) with:
+
+``` r
+install.packages("s2")
+```
+
+And the development version from [GitHub](https://github.com/) with:
+
+``` r
+# install.packages("remotes")
+remotes::install_github("r-spatial/s2")
+```
+
+## Example
+
+The s2 package provides geometry transformers and predicates similar to
+those found in [GEOS](https://trac.osgeo.org/geos/), except instead of
+assuming a planar geometry, s2’s functions work in latitude and
+longitude and assume a spherical geometry:
+
+``` r
+library(s2)
+
+s2_contains(
+ # polygon containing much of the northern hemisphere
+ "POLYGON ((-63.5 44.6, -149.75 61.20, 116.4 40.2, 13.5 52.51, -63.5 44.6))",
+ # ...should contain the north pole
+ "POINT (0 90)"
+)
+#> [1] TRUE
+```
+
+The [sf package](https://r-spatial.github.io/sf/) uses s2 for geographic
+coordinates with `sf::sf_use_s2(TRUE)`, and will become the default
+after sf version 1.0.0. The sf package also supports creating s2 vectors
+using `as_s2_geography()`:
+
+``` r
+library(dplyr)
+library(sf)
+
+nc_s2 <- read_sf(system.file("shape/nc.shp", package = "sf")) %>%
+ mutate(geometry = as_s2_geography(geometry)) %>%
+ as_tibble() %>%
+ select(NAME, geometry)
+
+nc_s2
+#> # A tibble: 100 x 2
+#> NAME geometry
+#> <chr> <s2_geography>
+#> 1 Ashe <POLYGON ((-81.4528885 36.2395859, -81.4310379 36.2607193, -81.4…
+#> 2 Alleghany <POLYGON ((-81.1766739 36.4154434, -81.1533661 36.4247398, -81.1…
+#> 3 Surry <POLYGON ((-80.4530106 36.2570877, -80.4353104 36.5510445, -80.6…
+#> 4 Currituck <MULTIPOLYGON (((-75.9419327 36.2943382, -75.9575119 36.2594528,…
+#> 5 Northampton <POLYGON ((-77.1419601 36.4170647, -77.1393204 36.4564781, -77.1…
+#> 6 Hertford <POLYGON ((-76.7074966 36.2661324, -76.7413483 36.3151665, -76.9…
+#> 7 Camden <POLYGON ((-76.0173492 36.3377304, -76.0328751 36.3359756, -76.0…
+#> 8 Gates <POLYGON ((-76.46035 36.3738976, -76.5024643 36.4522858, -76.498…
+#> 9 Warren <POLYGON ((-78.1347198 36.2365837, -78.1096268 36.2135086, -78.0…
+#> 10 Stokes <POLYGON ((-80.0240555 36.5450249, -80.0480957 36.5471344, -80.4…
+#> # … with 90 more rows
+```
+
+Use accessors to extract information about geometries:
+
+``` r
+nc_s2 %>%
+ mutate(
+ area = s2_area(geometry),
+ perimeter = s2_perimeter(geometry)
+ )
+#> # A tibble: 100 x 4
+#> NAME geometry area perimeter
+#> <chr> <s2_geography> <dbl> <dbl>
+#> 1 Ashe <POLYGON ((-81.4528885 36.2395859, -81.4310379 … 1.14e9 141627.
+#> 2 Alleghany <POLYGON ((-81.1766739 36.4154434, -81.1533661 … 6.11e8 119876.
+#> 3 Surry <POLYGON ((-80.4530106 36.2570877, -80.4353104 … 1.42e9 160458.
+#> 4 Currituck <MULTIPOLYGON (((-75.9419327 36.2943382, -75.95… 6.94e8 301644.
+#> 5 Northamp… <POLYGON ((-77.1419601 36.4170647, -77.1393204 … 1.52e9 211794.
+#> 6 Hertford <POLYGON ((-76.7074966 36.2661324, -76.7413483 … 9.68e8 160780.
+#> 7 Camden <POLYGON ((-76.0173492 36.3377304, -76.0328751 … 6.16e8 150430.
+#> 8 Gates <POLYGON ((-76.46035 36.3738976, -76.5024643 36… 9.03e8 123170.
+#> 9 Warren <POLYGON ((-78.1347198 36.2365837, -78.1096268 … 1.18e9 141073.
+#> 10 Stokes <POLYGON ((-80.0240555 36.5450249, -80.0480957 … 1.23e9 140583.
+#> # … with 90 more rows
+```
+
+Use predicates to subset vectors:
+
+``` r
+nc_s2 %>%
+ filter(s2_contains(geometry, "POINT (-80.9313 35.6196)"))
+#> # A tibble: 1 x 2
+#> NAME geometry
+#> <chr> <s2_geography>
+#> 1 Catawba <POLYGON ((-80.9312744 35.6195908, -81.0035782 35.6970558, -81.054779…
+```
+
+Use transformers to create new geometries:
+
+``` r
+nc_s2 %>%
+ mutate(geometry = s2_boundary(geometry))
+#> # A tibble: 100 x 2
+#> NAME geometry
+#> <chr> <s2_geography>
+#> 1 Ashe <LINESTRING (-81.4528885 36.2395859, -81.4310379 36.2607193, -81…
+#> 2 Alleghany <LINESTRING (-81.1766739 36.4154434, -81.1533661 36.4247398, -81…
+#> 3 Surry <LINESTRING (-80.4530106 36.2570877, -80.4353104 36.5510445, -80…
+#> 4 Currituck <MULTILINESTRING ((-75.9419327 36.2943382, -75.9575119 36.259452…
+#> 5 Northampton <LINESTRING (-77.1419601 36.4170647, -77.1393204 36.4564781, -77…
+#> 6 Hertford <LINESTRING (-76.7074966 36.2661324, -76.7413483 36.3151665, -76…
+#> 7 Camden <LINESTRING (-76.0173492 36.3377304, -76.0328751 36.3359756, -76…
+#> 8 Gates <LINESTRING (-76.46035 36.3738976, -76.5024643 36.4522858, -76.4…
+#> 9 Warren <LINESTRING (-78.1347198 36.2365837, -78.1096268 36.2135086, -78…
+#> 10 Stokes <LINESTRING (-80.0240555 36.5450249, -80.0480957 36.5471344, -80…
+#> # … with 90 more rows
+```
+
+Finally, use the WKB or WKT exporters to export to sf or some other
+package:
+
+``` r
+nc_s2 %>%
+ mutate(geometry = st_as_sfc(s2_as_binary(geometry))) %>%
+ st_as_sf()
+#> Simple feature collection with 100 features and 1 field
+#> geometry type: GEOMETRY
+#> dimension: XY
+#> bbox: xmin: -84.32385 ymin: 33.88199 xmax: -75.45698 ymax: 36.58965
+#> CRS: NA
+#> # A tibble: 100 x 2
+#> NAME geometry
+#> <chr> <GEOMETRY>
+#> 1 Ashe POLYGON ((-81.45289 36.23959, -81.43104 36.26072, -81.41233 36.26…
+#> 2 Alleghany POLYGON ((-81.17667 36.41544, -81.15337 36.42474, -81.1384 36.417…
+#> 3 Surry POLYGON ((-80.45301 36.25709, -80.43531 36.55104, -80.61105 36.55…
+#> 4 Currituck MULTIPOLYGON (((-75.94193 36.29434, -75.95751 36.25945, -75.91376…
+#> 5 Northampt… POLYGON ((-77.14196 36.41706, -77.13932 36.45648, -77.12733 36.47…
+#> 6 Hertford POLYGON ((-76.7075 36.26613, -76.74135 36.31517, -76.92408 36.392…
+#> 7 Camden POLYGON ((-76.01735 36.33773, -76.03288 36.33598, -76.04395 36.35…
+#> 8 Gates POLYGON ((-76.46035 36.3739, -76.50246 36.45229, -76.49834 36.503…
+#> 9 Warren POLYGON ((-78.13472 36.23658, -78.10963 36.21351, -78.05835 36.21…
+#> 10 Stokes POLYGON ((-80.02406 36.54502, -80.0481 36.54713, -80.43531 36.551…
+#> # … with 90 more rows
+```
+
+## Acknowledgment
+
+This project gratefully acknowledges financial
+[support](https://www.r-consortium.org/projects) from the
+
+<a href="https://www.r-consortium.org/projects/awarded-projects">
+<img src="man/figures/rc300.png" width="300" /> </a>
--- /dev/null
+#!/bin/sh
+rm -f src/Makevars configure.log autobrew
+rm `find src -name *.o`
--- /dev/null
+# Anticonf (tm) script by Jeroen Ooms (2020)
+# This script will query 'pkg-config' for the required cflags and ldflags.
+# If pkg-config is unavailable or does not find the library, try setting
+# INCLUDE_DIR and LIB_DIR manually via e.g:
+# R CMD INSTALL --configure-vars='INCLUDE_DIR=/.../include LIB_DIR=/.../lib'
+
+# Library settings
+PKG_CONFIG_NAME="openssl"
+PKG_DEB_NAME="libssl-dev"
+PKG_RPM_NAME="openssl-devel"
+PKG_CSW_NAME="libssl_dev"
+PKG_BREW_NAME="openssl@1.1"
+PKG_TEST_FILE="tools/version.c"
+PKG_LIBS="-lssl -lcrypto"
+PKG_CFLAGS=""
+
+# Use pkg-config if available
+pkg-config ${PKG_CONFIG_NAME} --atleast-version=1.0 2>/dev/null
+if [ $? -eq 0 ]; then
+ PKGCONFIG_CFLAGS=`pkg-config --cflags ${PKG_CONFIG_NAME}`
+ PKGCONFIG_LIBS=`pkg-config --libs ${PKG_CONFIG_NAME}`
+fi
+
+# Note that cflags may be empty in case of success
+if [ "$INCLUDE_DIR" ] || [ "$LIB_DIR" ]; then
+ echo "Found INCLUDE_DIR and/or LIB_DIR!"
+ PKG_CFLAGS="-I$INCLUDE_DIR $PKG_CFLAGS"
+ PKG_LIBS="-L$LIB_DIR $PKG_LIBS"
+elif [ "$PKGCONFIG_CFLAGS" ] || [ "$PKGCONFIG_LIBS" ]; then
+ echo "Found pkg-config cflags and libs!"
+ PKG_CFLAGS=${PKGCONFIG_CFLAGS}
+ PKG_LIBS=${PKGCONFIG_LIBS}
+elif [ `uname` = "Darwin" ]; then
+ brew --version 2>/dev/null
+ if [ $? -eq 0 ]; then
+ BREWDIR=`brew --prefix`
+ PKG_CFLAGS="-I$BREWDIR/opt/openssl/include"
+ PKG_LIBS="-L$BREWDIR/opt/openssl/lib $PKG_LIBS"
+ else
+ curl -sfL "https://autobrew.github.io/scripts/$PKG_BREW_NAME" > autobrew
+ . autobrew
+ fi
+fi
+
+# Find compiler
+CC=`${R_HOME}/bin/R CMD config CC`
+CFLAGS=`${R_HOME}/bin/R CMD config CFLAGS`
+CPPFLAGS=`${R_HOME}/bin/R CMD config CPPFLAGS`
+
+# For debugging
+echo "Testing compiler using PKG_CFLAGS=$PKG_CFLAGS"
+
+# Test configuration
+${CC} ${CPPFLAGS} ${PKG_CFLAGS} ${CFLAGS} -E ${PKG_TEST_FILE} >/dev/null 2>configure.log
+
+# Customize the error
+if [ $? -ne 0 ]; then
+ echo "--------------------------- [ANTICONF] --------------------------------"
+ echo "Configuration failed because $PKG_CONFIG_NAME was not found. Try installing:"
+ echo " * deb: $PKG_DEB_NAME (Debian, Ubuntu, etc)"
+ echo " * rpm: $PKG_RPM_NAME (Fedora, CentOS, RHEL)"
+ echo " * csw: $PKG_CSW_NAME (Solaris)"
+ echo " * brew: $PKG_BREW_NAME (Mac OSX)"
+ echo "If $PKG_CONFIG_NAME is already installed, check that 'pkg-config' is in your"
+ echo "PATH and PKG_CONFIG_PATH contains a $PKG_CONFIG_NAME.pc file. If pkg-config"
+ echo "is unavailable you can set INCLUDE_DIR and LIB_DIR manually via:"
+ echo "R CMD INSTALL --configure-vars='INCLUDE_DIR=... LIB_DIR=...'"
+ echo "-------------------------- [ERROR MESSAGE] ---------------------------"
+ cat configure.log
+ echo "--------------------------------------------------------------------"
+ exit 1
+fi
+
+# Try to link against the correct OpenSSL version
+if [ -z "$AUTOBREW" ]; then
+SONAME=`${CC} -E ${PKG_CFLAGS} src/tests/soname.h | sh | xargs`
+if [ "$SONAME" ]; then
+if [ `uname` = "Darwin" ]; then
+ PKG_LIBS_VERSIONED=`echo "${PKG_LIBS}" | sed "s/-lssl/-lssl.${SONAME}/" | sed "s/-lcrypto/-lcrypto.${SONAME}/"`
+else
+ PKG_LIBS_VERSIONED=`echo "${PKG_LIBS}" | sed "s/-lssl/-l:libssl.so.${SONAME}/" | sed "s/-lcrypto/-l:libcrypto.so.${SONAME}/"`
+fi
+
+# Test if versioned linking works
+${CC} ${PKG_CFLAGS} src/tests/main.c ${PKG_LIBS_VERSIONED} -o src/main.exe 2>/dev/null
+if [ $? -eq 0 ]; then PKG_LIBS="${PKG_LIBS_VERSIONED}"; fi
+
+# Suppress opensslv3 warnings for now
+if [ "$SONAME" = "3" ]; then
+PKG_CFLAGS="$PKG_CFLAGS -DOPENSSL_SUPPRESS_DEPRECATED"
+fi
+
+fi #SONAME
+fi #AUTOBREW
+
+# Define system endianness (compile-time endianness using system/compiler
+# defines isn't detected on Solaris)
+# based on endian detection from the feather package by @hadley
+R_ENDIAN=`${R_HOME}/bin/Rscript -e 'cat(.Platform$endian)'`
+# Trim off any warning messages that Rscript appends in front of the platform endianness
+R_ENDIAN=`expr "$R_ENDIAN" : '.*\(little\)$'`
+SYS_ENDIAN=""
+if [ "$R_ENDIAN" = "little" ]; then
+ PKG_CFLAGS="$PKG_CFLAGS -DIS_LITTLE_ENDIAN"
+else
+ PKG_CFLAGS="$PKG_CFLAGS -DIS_BIG_ENDIAN"
+fi
+
+echo "Using PKG_LIBS=$PKG_LIBS"
+echo "Using PKG_CFLAGS=$PKG_CFLAGS"
+
+
+# Write to Makevars
+sed -e "s|@cflags@|$PKG_CFLAGS|" -e "s|@libs@|$PKG_LIBS|" src/Makevars.in > src/Makevars
+
+# Success
+exit 0
--- /dev/null
+
+#ifndef CPP_COMPAT_H
+#define CPP_COMPAT_H
+
+#include <streambuf>
+
+void cpp_compat_printf(const char* fmt, ...);
+[[ noreturn ]] void cpp_compat_abort();
+[[ noreturn ]] void cpp_compat_exit(int code);
+int cpp_compat_random();
+void cpp_compat_srandom(int seed);
+
+extern std::ostream& cpp_compat_cerr;
+extern std::ostream& cpp_compat_cout;
+
+#endif
--- /dev/null
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2__FP_CONTRACT_OFF_H_
+#define S2__FP_CONTRACT_OFF_H_
+
+// Turn off the fused multiply-add optimization ("fp-contract"). With
+// fp-contract on, any expression of the form "a * b + c" has two possible
+// results, and the compiler is free to choose either of them. Effectively
+// this makes it impossible to write deterministic functions that involve
+// floating-point math.
+//
+// S2 requires deterministic arithmetic for correctness. We need to turn off
+// fp-contract for the entire compilation unit, because S2 has public inline
+// functions, and the optimization is controlled by the setting in effect when
+// inline functions are instantiated (not when they are defined).
+//
+// Note that there is a standard C pragma to turn off FP contraction:
+// #pragma STDC FP_CONTRACT OFF
+// but it is not implemented in GCC because the standard pragma allows control
+// at the level of compound statements rather than entire functions.
+//
+// This file may be included with other files in any order, as long as it
+// appears before the first non-inline function definition. It is
+// named with an underscore so that it is included first among the S2 headers.
+
+// TODO(compiler-team): Figure out how to do this in a portable way.
+#if defined(HAVE_ARMEABI_V7A)
+// Some android builds use a buggy compiler that runs out of memory while
+// parsing the pragma (--cpu=armeabi-v7a).
+
+#elif defined(__ANDROID__)
+// Other android builds use a buggy compiler that crashes with an internal
+// error (Android NDK R9).
+
+#elif defined(__clang__)
+// Clang supports the standard C++ pragma for turning off this optimization.
+#pragma STDC FP_CONTRACT OFF
+
+#elif defined(__GNUC__)
+// GCC defines its own pragma that operates at the function level rather than
+// the statement level.
+#pragma GCC optimize("fp-contract=off")
+#endif
+
+#endif // S2__FP_CONTRACT_OFF_H_
--- /dev/null
+// Copyright 2009 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+
+//
+// Various Google-specific casting templates.
+//
+// This code is compiled directly on many platforms, including client
+// platforms like Windows, Mac, and embedded systems. Before making
+// any changes here, make sure that you're not breaking any platforms.
+//
+
+#ifndef S2_BASE_CASTS_H_
+#define S2_BASE_CASTS_H_
+
+#include <cassert> // for use with down_cast<>
+#include <climits> // for enumeration casts and tests
+#include <type_traits>
+
+#include "s2/third_party/absl/base/casts.h"
+#include "s2/third_party/absl/base/macros.h"
+
+// An "upcast", i.e. a conversion from a pointer to an object to a pointer to a
+// base subobject, always succeeds if the base is unambiguous and accessible,
+// and so it's fine to use implicit_cast.
+//
+// A "downcast", i.e. a conversion from a pointer to an object to a pointer
+// to a more-derived object that may contain the original object as a base
+// subobject, cannot safely be done using static_cast, because you do not
+// generally know whether the source object is really the base subobject of
+// a containing, more-derived object of the target type. Thus, when you
+// downcast in a polymorphic type hierarchy, you should use the following
+// function template.
+//
+// In debug mode, we use dynamic_cast to double-check whether the downcast is
+// legal (we die if it's not). In normal mode, we do the efficient static_cast
+// instead. Thus, it's important to test in debug mode to make sure the cast is
+// legal!
+//
+// This is the only place in the codebase we should use dynamic_cast.
+// In particular, you should NOT use dynamic_cast for RTTI, e.g. for
+// code like this:
+// if (auto* p = dynamic_cast<Subclass1*>(foo)) HandleASubclass1Object(p);
+// if (auto* p = dynamic_cast<Subclass2*>(foo)) HandleASubclass2Object(p);
+// You should design the code some other way not to need this.
+
+template<typename To, typename From> // use like this: down_cast<T*>(foo);
+inline To down_cast(From* f) { // so we only accept pointers
+ static_assert(
+ (std::is_base_of<From, typename std::remove_pointer<To>::type>::value),
+ "target type not derived from source type");
+
+ // We skip the assert and hence the dynamic_cast if RTTI is disabled.
+#if !defined(__GNUC__) || defined(__GXX_RTTI)
+ // Uses RTTI in dbg and fastbuild. asserts are disabled in opt builds.
+ assert(f == nullptr || dynamic_cast<To>(f) != nullptr);
+#endif // !defined(__GNUC__) || defined(__GXX_RTTI)
+
+ return static_cast<To>(f);
+}
+
+// Overload of down_cast for references. Use like this: down_cast<T&>(foo).
+// The code is slightly convoluted because we're still using the pointer
+// form of dynamic cast. (The reference form throws an exception if it
+// fails.)
+//
+// There's no need for a special const overload either for the pointer
+// or the reference form. If you call down_cast with a const T&, the
+// compiler will just bind From to const T.
+template<typename To, typename From>
+inline To down_cast(From& f) {
+ static_assert(
+ std::is_lvalue_reference<To>::value, "target type not a reference");
+ static_assert(
+ (std::is_base_of<From, typename std::remove_reference<To>::type>::value),
+ "target type not derived from source type");
+
+ // We skip the assert and hence the dynamic_cast if RTTI is disabled.
+#if !defined(__GNUC__) || defined(__GXX_RTTI)
+ // RTTI: debug mode only
+ assert(dynamic_cast<typename std::remove_reference<To>::type*>(&f) !=
+ nullptr);
+#endif // !defined(__GNUC__) || defined(__GXX_RTTI)
+
+ return static_cast<To>(f);
+}
+
+// **** Enumeration Casts and Tests
+//
+// C++ requires that the value of an integer that is converted to an
+// enumeration be within the value bounds of the enumeration. Modern
+// compilers can and do take advantage of this requirement to optimize
+// programs. So, using a raw static_cast with enums can be bad. See
+//
+// The following templates and macros enable casting from an int to an enum
+// with checking against the appropriate bounds. First, when defining an
+// enumeration, identify the limits of the values of its enumerators.
+//
+// enum A { A_min = -18, A_max = 33 };
+// MAKE_ENUM_LIMITS(A, A_min, A_max)
+//
+// Convert an int to an enum in one of two ways. The prefered way is a
+// tight conversion, which ensures that A_min <= value <= A_max.
+//
+// A var = tight_enum_cast<A>(3);
+//
+// However, the C++ language defines the set of possible values for an
+// enumeration to be essentially the range of a bitfield that can represent
+// all the enumerators, i.e. those within the nearest containing power
+// of two. In the example above, the nearest positive power of two is 64,
+// and so the upper bound is 63. The nearest negative power of two is
+// -32 and so the lower bound is -32 (two's complement), which is upgraded
+// to match the upper bound, becoming -64. The values within this range
+// of -64 to 63 are valid, according to the C++ standard. You can cast
+// values within this range as follows.
+//
+// A var = loose_enum_cast<A>(45);
+//
+// These casts will log a message if the value does not reside within the
+// specified range, and will be fatal when in debug mode.
+//
+// For those times when an assert too strong, there are test functions.
+//
+// bool var = tight_enum_test<A>(3);
+// bool var = loose_enum_test<A>(45);
+//
+// For code that needs to use the enumeration value if and only if
+// it is good, there is a function that both tests and casts.
+//
+// int i = ....;
+// A var;
+// if (tight_enum_test_cast<A>(i, &var))
+// .... // use valid var with value as indicated by i
+// else
+// .... // handle invalid enum cast
+//
+// The enum test/cast facility is currently limited to enumerations that
+// fit within an int. It is also limited to two's complement ints.
+
+// ** Implementation Description
+//
+// The enum_limits template class captures the minimum and maximum
+// enumerator. All uses of this template are intended to be of
+// specializations, so the generic has a field to identify itself as
+// not specialized. The test/cast templates assert specialization.
+
+template <typename Enum>
+class enum_limits {
+ public:
+ static const Enum min_enumerator = 0;
+ static const Enum max_enumerator = 0;
+ static const bool is_specialized = false;
+};
+
+// Now we define the macro to define the specialization for enum_limits.
+// The specialization checks that the enumerators fit within an int.
+// This checking relies on integral promotion.
+
+#define MAKE_ENUM_LIMITS(ENUM_TYPE, ENUM_MIN, ENUM_MAX) \
+template <> \
+class enum_limits<ENUM_TYPE> { \
+public: \
+ static const ENUM_TYPE min_enumerator = ENUM_MIN; \
+ static const ENUM_TYPE max_enumerator = ENUM_MAX; \
+ static const bool is_specialized = true; \
+ static_assert(ENUM_MIN >= INT_MIN, "enumerator too negative for int"); \
+ static_assert(ENUM_MAX <= INT_MAX, "enumerator too positive for int"); \
+};
+
+// The loose enum test/cast is actually the more complicated one,
+// because of the problem of finding the bounds.
+//
+// The unary upper bound, ub, on a positive number is its positive
+// saturation, i.e. for a value v within pow(2,k-1) <= v < pow(2,k),
+// the upper bound is pow(2,k)-1.
+//
+// The unary lower bound, lb, on a negative number is its negative
+// saturation, i.e. for a value v within -pow(2,k) <= v < -pow(2,k-1),
+// the lower bound is -pow(2,k).
+//
+// The actual bounds are (1) the binary upper bound over the maximum
+// enumerator and the one's complement of a negative minimum enumerator
+// and (2) the binary lower bound over the minimum enumerator and the
+// one's complement of the positive maximum enumerator, except that if no
+// enumerators are negative, the lower bound is zero.
+//
+// The algorithm relies heavily on the observation that
+//
+// a,b>0 then ub(a,b) == ub(a) | ub(b) == ub(a|b)
+// a,b<0 then lb(a,b) == lb(a) & lb(b) == lb(a&b)
+//
+// Note that the compiler will boil most of this code away
+// because of value propagation on the constant enumerator bounds.
+
+template <typename Enum>
+inline bool loose_enum_test(int e_val) {
+ static_assert(enum_limits<Enum>::is_specialized, "missing MAKE_ENUM_LIMITS");
+ const Enum e_min = enum_limits<Enum>::min_enumerator;
+ const Enum e_max = enum_limits<Enum>::max_enumerator;
+ static_assert(sizeof(e_val) == 4 || sizeof(e_val) == 8,
+ "unexpected int size");
+
+ // Find the unary bounding negative number of e_min and e_max.
+
+ // Find the unary bounding negative number of e_max.
+ // This would be b_min = e_max < 0 ? e_max : ~e_max,
+ // but we want to avoid branches to help the compiler.
+ int e_max_sign = e_max >> (sizeof(e_val)*8 - 1);
+ int b_min = ~e_max_sign ^ e_max;
+
+ // Find the binary bounding negative of both e_min and e_max.
+ b_min &= e_min;
+
+ // However, if e_min is positive, the result will be positive.
+ // Now clear all bits right of the most significant clear bit,
+ // which is a negative saturation for negative numbers.
+ // In the case of positive numbers, this is flush to zero.
+ b_min &= b_min >> 1;
+ b_min &= b_min >> 2;
+ b_min &= b_min >> 4;
+ b_min &= b_min >> 8;
+ b_min &= b_min >> 16;
+#if INT_MAX > 2147483647
+ b_min &= b_min >> 32;
+#endif
+
+ // Find the unary bounding positive number of e_max.
+ int b_max = e_max_sign ^ e_max;
+
+ // Find the binary bounding positive number of that
+ // and the unary bounding positive number of e_min.
+ int e_min_sign = e_min >> (sizeof(e_val)*8 - 1);
+ b_max |= e_min_sign ^ e_min;
+
+ // Now set all bits right of the most significant set bit,
+ // which is a positive saturation for positive numbers.
+ b_max |= b_max >> 1;
+ b_max |= b_max >> 2;
+ b_max |= b_max >> 4;
+ b_max |= b_max >> 8;
+ b_max |= b_max >> 16;
+#if INT_MAX > 2147483647
+ b_max |= b_max >> 32;
+#endif
+
+ // Finally test the bounds.
+ return b_min <= e_val && e_val <= b_max;
+}
+
+template <typename Enum>
+inline bool tight_enum_test(int e_val) {
+ static_assert(enum_limits<Enum>::is_specialized, "missing MAKE_ENUM_LIMITS");
+ const Enum e_min = enum_limits<Enum>::min_enumerator;
+ const Enum e_max = enum_limits<Enum>::max_enumerator;
+ return e_min <= e_val && e_val <= e_max;
+}
+
+template <typename Enum>
+inline bool loose_enum_test_cast(int e_val, Enum* e_var) {
+ if (loose_enum_test<Enum>(e_val)) {
+ *e_var = static_cast<Enum>(e_val);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+template <typename Enum>
+inline bool tight_enum_test_cast(int e_val, Enum* e_var) {
+ if (tight_enum_test<Enum>(e_val)) {
+ *e_var = static_cast<Enum>(e_val);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// The plain casts require logging, and we get header recursion if
+// it is done directly. So, we do it indirectly.
+// The following function is defined in logging.cc.
+
+namespace base {
+namespace internal {
+
+void WarnEnumCastError(int value_of_int);
+
+} // namespace internal
+} // namespace base
+
+template <typename Enum>
+inline Enum loose_enum_cast(int e_val) {
+ if (!loose_enum_test<Enum>(e_val)) {
+ base::internal::WarnEnumCastError(e_val);
+ }
+ return static_cast<Enum>(e_val);
+}
+
+template <typename Enum>
+inline Enum tight_enum_cast(int e_val) {
+ if (!tight_enum_test<Enum>(e_val)) {
+ base::internal::WarnEnumCastError(e_val);
+ }
+ return static_cast<Enum>(e_val);
+}
+
+#endif // S2_BASE_CASTS_H_
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef S2_BASE_COMMANDLINEFLAGS_H_
+#define S2_BASE_COMMANDLINEFLAGS_H_
+
+#ifdef S2_USE_GFLAGS
+
+#include <gflags/gflags.h>
+
+#else // !defined(S2_USE_GFLAGS)
+
+#include <string>
+
+#include "s2/base/integral_types.h"
+
+#define DEFINE_bool(name, default_value, description) \
+ bool FLAGS_##name = default_value
+#define DECLARE_bool(name) \
+ extern bool FLAGS_##name
+
+#define DEFINE_double(name, default_value, description) \
+ double FLAGS_##name = default_value
+#define DECLARE_double(name) \
+ extern double FLAGS_##name
+
+#define DEFINE_int32(name, default_value, description) \
+ int32 FLAGS_##name = default_value
+#define DECLARE_int32(name) \
+ extern int32 FLAGS_##name
+
+#define DEFINE_string(name, default_value, description) \
+ std::string FLAGS_##name = default_value
+#define DECLARE_string(name) \
+ extern std::string FLAGS_##name
+
+#endif // !defined(S2_USE_GFLAGS)
+
+#endif // S2_BASE_COMMANDLINEFLAGS_H_
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef S2_BASE_INTEGRAL_TYPES_H_
+#define S2_BASE_INTEGRAL_TYPES_H_
+
+using int8 = signed char;
+using int16 = short;
+using int32 = int;
+using int64 = long long;
+
+using uint8 = unsigned char;
+using uint16 = unsigned short;
+using uint32 = unsigned int;
+using uint64 = unsigned long long;
+
+using uword_t = unsigned long;
+
+#endif // S2_BASE_INTEGRAL_TYPES_H_
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef S2_BASE_LOG_SEVERITY_H_
+#define S2_BASE_LOG_SEVERITY_H_
+
+#ifdef S2_USE_GLOG
+
+#include <glog/log_severity.h>
+
+#else // !defined(S2_USE_GLOG)
+
+#include "s2/third_party/absl/base/log_severity.h"
+
+// Stay compatible with glog.
+namespace google {
+
+#ifdef NDEBUG
+constexpr bool DEBUG_MODE = false;
+#else
+constexpr bool DEBUG_MODE = true;
+#endif
+
+} // namespace google
+
+#endif // !defined(S2_USE_GLOG)
+
+#endif // S2_BASE_LOG_SEVERITY_H_
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef S2_BASE_LOGGING_H_
+#define S2_BASE_LOGGING_H_
+#include "cpp-compat.h"
+
+#ifdef S2_USE_GLOG
+
+#include <glog/logging.h>
+
+// The names CHECK, etc. are too common and may conflict with other
+// packages. We use S2_CHECK to make it easier to switch to
+// something other than GLOG for logging.
+
+#define S2_LOG LOG
+#define S2_LOG_IF LOG_IF
+#define S2_DLOG_IF DLOG_IF
+
+#define S2_CHECK CHECK
+#define S2_CHECK_EQ CHECK_EQ
+#define S2_CHECK_NE CHECK_NE
+#define S2_CHECK_LT CHECK_LT
+#define S2_CHECK_LE CHECK_LE
+#define S2_CHECK_GT CHECK_GT
+#define S2_CHECK_GE CHECK_GE
+
+#define S2_DCHECK DCHECK
+#define S2_DCHECK_EQ DCHECK_EQ
+#define S2_DCHECK_NE DCHECK_NE
+#define S2_DCHECK_LT DCHECK_LT
+#define S2_DCHECK_LE DCHECK_LE
+#define S2_DCHECK_GT DCHECK_GT
+#define S2_DCHECK_GE DCHECK_GE
+
+#define S2_VLOG VLOG
+#define S2_VLOG_IS_ON VLOG_IS_ON
+
+#else // !defined(S2_USE_GLOG)
+
+#include <iostream>
+
+#include "s2/base/log_severity.h"
+#include "s2/third_party/absl/base/attributes.h"
+#include "s2/third_party/absl/base/log_severity.h"
+
+class S2LogMessage {
+ public:
+ S2LogMessage(const char* file, int line,
+ absl::LogSeverity severity, std::ostream& stream)
+ : severity_(severity), stream_(stream) {
+ if (enabled()) {
+ stream_ << file << ":" << line << " "
+ << absl::LogSeverityName(severity) << " ";
+ }
+ }
+ ~S2LogMessage() { if (enabled()) stream_ << std::endl; }
+
+ std::ostream& stream() { return stream_; }
+
+ // silences an 'unused member' compiler warning
+ absl::LogSeverity severity() { return severity_; }
+
+ private:
+ bool enabled() const {
+#ifdef ABSL_MIN_LOG_LEVEL
+ return (static_cast<int>(severity_) >= ABSL_MIN_LOG_LEVEL ||
+ severity_ >= absl::LogSeverity::kFatal);
+#else
+ return true;
+#endif
+ }
+
+ absl::LogSeverity severity_;
+ std::ostream& stream_;
+};
+
+// Same as S2LogMessage, but destructor is marked no-return to avoid
+// "no return value warnings" in functions that return non-void.
+class S2FatalLogMessage : public S2LogMessage {
+ public:
+ S2FatalLogMessage(const char* file, int line,
+ absl::LogSeverity severity, std::ostream& stream)
+ ABSL_ATTRIBUTE_COLD
+ : S2LogMessage(file, line, severity, stream) {}
+ ABSL_ATTRIBUTE_NORETURN ~S2FatalLogMessage() { cpp_compat_abort(); }
+};
+
+// Logging stream that does nothing.
+struct S2NullStream {
+ template <typename T>
+ S2NullStream& operator<<(const T& v) { return *this; }
+};
+
+// Used to suppress "unused value" warnings.
+struct S2LogMessageVoidify {
+ // Must have precedence lower than << but higher than ?:.
+ void operator&(std::ostream&) {}
+};
+
+#define S2_LOG_MESSAGE_(LogMessageClass, log_severity) \
+ LogMessageClass(__FILE__, __LINE__, log_severity, cpp_compat_cerr)
+#define S2_LOG_INFO \
+ S2_LOG_MESSAGE_(S2LogMessage, absl::LogSeverity::kInfo)
+#define S2_LOG_WARNING \
+ S2_LOG_MESSAGE_(S2LogMessage, absl::LogSeverity::kWarning)
+#define S2_LOG_ERROR \
+ S2_LOG_MESSAGE_(S2LogMessage, absl::LogSeverity::kError)
+#define S2_LOG_FATAL \
+ S2_LOG_MESSAGE_(S2FatalLogMessage, absl::LogSeverity::kFatal)
+#ifndef NDEBUG
+#define S2_LOG_DFATAL S2_LOG_FATAL
+#else
+#define S2_LOG_DFATAL S2_LOG_ERROR
+#endif
+
+#define S2_LOG(severity) S2_LOG_##severity.stream()
+
+// Implementing this as if (...) {} else S2_LOG(...) will cause dangling else
+// warnings when someone does if (...) S2_LOG_IF(...), so do this tricky
+// thing instead.
+#define S2_LOG_IF(severity, condition) \
+ !(condition) ? (void)0 : S2LogMessageVoidify() & S2_LOG(severity)
+
+#define S2_CHECK(condition) \
+ S2_LOG_IF(FATAL, ABSL_PREDICT_FALSE(!(condition))) \
+ << ("Check failed: " #condition " ")
+
+#ifndef NDEBUG
+
+#define S2_DLOG_IF S2_LOG_IF
+#define S2_DCHECK S2_CHECK
+
+#else // defined(NDEBUG)
+
+#define S2_DLOG_IF(severity, condition) \
+ while (false && (condition)) S2NullStream()
+#define S2_DCHECK(condition) \
+ while (false && (condition)) S2NullStream()
+
+#endif // defined(NDEBUG)
+
+#define S2_CHECK_OP(op, val1, val2) S2_CHECK((val1) op (val2))
+#define S2_CHECK_EQ(val1, val2) S2_CHECK_OP(==, val1, val2)
+#define S2_CHECK_NE(val1, val2) S2_CHECK_OP(!=, val1, val2)
+#define S2_CHECK_LT(val1, val2) S2_CHECK_OP(<, val1, val2)
+#define S2_CHECK_LE(val1, val2) S2_CHECK_OP(<=, val1, val2)
+#define S2_CHECK_GT(val1, val2) S2_CHECK_OP(>, val1, val2)
+#define S2_CHECK_GE(val1, val2) S2_CHECK_OP(>=, val1, val2)
+
+#define S2_DCHECK_OP(op, val1, val2) S2_DCHECK((val1) op (val2))
+#define S2_DCHECK_EQ(val1, val2) S2_DCHECK_OP(==, val1, val2)
+#define S2_DCHECK_NE(val1, val2) S2_DCHECK_OP(!=, val1, val2)
+#define S2_DCHECK_LT(val1, val2) S2_DCHECK_OP(<, val1, val2)
+#define S2_DCHECK_LE(val1, val2) S2_DCHECK_OP(<=, val1, val2)
+#define S2_DCHECK_GT(val1, val2) S2_DCHECK_OP(>, val1, val2)
+#define S2_DCHECK_GE(val1, val2) S2_DCHECK_OP(>=, val1, val2)
+
+// We don't support VLOG.
+#define S2_VLOG(verbose_level) S2NullStream()
+#define S2_VLOG_IS_ON(verbose_level) (false)
+
+#endif // !defined(S2_USE_GLOG)
+
+#endif // S2_BASE_LOGGING_H_
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef S2_BASE_MUTEX_H_
+#define S2_BASE_MUTEX_H_
+
+#include <condition_variable>
+#include <mutex>
+
+namespace absl {
+
+class Mutex {
+ public:
+ Mutex() = default;
+ ~Mutex() = default;
+ Mutex(Mutex const&) = delete;
+ Mutex& operator=(Mutex const&) = delete;
+
+ inline void Lock() { mutex_.lock(); }
+ inline void Unlock() { mutex_.unlock(); }
+
+ private:
+ std::mutex mutex_;
+
+ friend class CondVar;
+};
+
+class CondVar {
+ public:
+ CondVar() = default;
+ ~CondVar() = default;
+ CondVar(CondVar const&) = delete;
+ CondVar& operator=(CondVar const&) = delete;
+
+ inline void Wait(Mutex* mu) {
+ std::unique_lock<std::mutex> lock(mu->mutex_, std::adopt_lock);
+ cond_var_.wait(lock);
+ lock.release();
+ }
+ inline void Signal() { cond_var_.notify_one(); }
+ inline void SignalAll() { cond_var_.notify_all(); }
+
+ private:
+ std::condition_variable cond_var_;
+};
+
+} // namespace absl
+
+#endif // S2_BASE_MUTEX_H_
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef S2_BASE_PORT_H_
+#define S2_BASE_PORT_H_
+
+// This file contains things that are not used in third_party/absl but needed by
+// - Platform specific requirement
+// - MSVC
+// - Utility macros
+// - Endianness
+// - Hash
+// - Global variables
+// - Type alias
+// - Predefined system/language macros
+// - Predefined system/language functions
+// - Performance optimization (alignment)
+// - Obsolete
+
+#include <cassert>
+#include <climits>
+#include <cstdlib>
+#include <cstring>
+
+#include "s2/base/integral_types.h"
+#include "s2/third_party/absl/base/config.h"
+#include "s2/third_party/absl/base/port.h"
+
+#ifdef SWIG
+%include "third_party/absl/base/port.h"
+#endif
+
+// -----------------------------------------------------------------------------
+// MSVC Specific Requirements
+// -----------------------------------------------------------------------------
+
+#ifdef _MSC_VER /* if Visual C++ */
+
+#include <winsock2.h> // Must come before <windows.h>
+#include <intrin.h>
+#include <process.h> // _getpid()
+#include <windows.h>
+#undef ERROR
+#undef DELETE
+#undef DIFFERENCE
+#define STDIN_FILENO 0
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+#define S_IRUSR 00400
+#define S_IWUSR 00200
+#define S_IXUSR 00100
+#define S_IRGRP 00040
+#define S_IWGRP 00020
+#define S_IXGRP 00010
+#define S_IROTH 00004
+#define S_IWOTH 00002
+#define S_IXOTH 00001
+
+// This compiler flag can be easily overlooked on MSVC.
+// _CHAR_UNSIGNED gets set with the /J flag.
+#ifndef _CHAR_UNSIGNED
+#error chars must be unsigned! Use the /J flag on the compiler command line. // NOLINT
+#endif
+
+// Allow comparisons between signed and unsigned values.
+//
+// Lots of Google code uses this pattern:
+// for (int i = 0; i < container.size(); ++i)
+// Since size() returns an unsigned value, this warning would trigger
+// frequently. Very few of these instances are actually bugs since containers
+// rarely exceed MAX_INT items. Unfortunately, there are bugs related to
+// signed-unsigned comparisons that have been missed because we disable this
+// warning. For example:
+// const long stop_time = os::GetMilliseconds() + kWaitTimeoutMillis;
+// while (os::GetMilliseconds() <= stop_time) { ... }
+#pragma warning(disable : 4018) // level 3
+#pragma warning(disable : 4267) // level 3
+
+// Don't warn about unused local variables.
+//
+// extension to silence particular instances of this warning. There's no way
+// to define ABSL_ATTRIBUTE_UNUSED to quiet particular instances of this warning
+// in VC++, so we disable it globally. Currently, there aren't many false
+// positives, so perhaps we can address those in the future and re-enable these
+// warnings, which sometimes catch real bugs.
+#pragma warning(disable : 4101) // level 3
+
+// Allow initialization and assignment to a smaller type without warnings about
+// possible loss of data.
+//
+// There is a distinct warning, 4267, that warns about size_t conversions to
+// smaller types, but we don't currently disable that warning.
+//
+// Correct code can be written in such a way as to avoid false positives
+// by making the conversion explicit, but Google code isn't usually that
+// verbose. There are too many false positives to address at this time. Note
+// that this warning triggers at levels 2, 3, and 4 depending on the specific
+// type of conversion. By disabling it, we not only silence minor narrowing
+// conversions but also serious ones.
+#pragma warning(disable : 4244) // level 2, 3, and 4
+
+// Allow silent truncation of double to float.
+//
+// Silencing this warning has caused us to miss some subtle bugs.
+#pragma warning(disable : 4305) // level 1
+
+// Allow a constant to be assigned to a type that is too small.
+//
+// I don't know why we allow this at all. I can't think of a case where this
+// wouldn't be a bug, but enabling the warning breaks many builds today.
+#pragma warning(disable : 4307) // level 2
+
+// Allow passing the this pointer to an initializer even though it refers
+// to an uninitialized object.
+//
+// Some observer implementations rely on saving the this pointer. Those are
+// safe because the pointer is not dereferenced until after the object is fully
+// constructed. This could however, obscure other instances. In the future, we
+// should look into disabling this warning locally rather globally.
+#pragma warning(disable : 4355) // level 1 and 4
+
+// Allow implicit coercion from an integral type to a bool.
+//
+// These could be avoided by making the code more explicit, but that's never
+// been the style here, so there would be many false positives. It's not
+// obvious if a true positive would ever help to find an actual bug.
+#pragma warning(disable : 4800) // level 3
+
+#endif // _MSC_VER
+
+// -----------------------------------------------------------------------------
+// Utility Macros
+// -----------------------------------------------------------------------------
+
+// OS_IOS
+#if defined(__APPLE__)
+// Currently, blaze supports iOS yet doesn't define a flag. Mac users have
+// traditionally defined OS_IOS themselves via other build systems, since mac
+// hasn't been supported by blaze.
+// TODO(user): Remove this when all toolchains make the proper defines.
+#include <TargetConditionals.h>
+#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
+#ifndef OS_IOS
+#define OS_IOS 1
+#endif
+#endif // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
+#endif // defined(__APPLE__)
+
+// __GLIBC_PREREQ
+#if defined __linux__
+// GLIBC-related macros.
+#include <features.h>
+
+#ifndef __GLIBC_PREREQ
+#define __GLIBC_PREREQ(a, b) 0 // not a GLIBC system
+#endif
+#endif // __linux__
+
+// STATIC_ANALYSIS
+// Klocwork static analysis tool's C/C++ complier kwcc
+#if defined(__KLOCWORK__)
+#define STATIC_ANALYSIS
+#endif // __KLOCWORK__
+
+// SIZEOF_MEMBER, OFFSETOF_MEMBER
+#define SIZEOF_MEMBER(t, f) sizeof(reinterpret_cast<t *>(4096)->f)
+
+#define OFFSETOF_MEMBER(t, f) \
+ (reinterpret_cast<char *>(&(reinterpret_cast<t *>(16)->f)) - \
+ reinterpret_cast<char *>(16))
+
+// LANG_CXX11
+// GXX_EXPERIMENTAL_CXX0X is defined by gcc and clang up to at least
+// gcc-4.7 and clang-3.1 (2011-12-13). __cplusplus was defined to 1
+// in gcc before 4.7 (Crosstool 16) and clang before 3.1, but is
+// defined according to the language version in effect thereafter.
+// Microsoft Visual Studio 14 (2015) sets __cplusplus==199711 despite
+// reasonably good C++11 support, so we set LANG_CXX for it and
+// newer versions (_MSC_VER >= 1900).
+#if (defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L || \
+ (defined(_MSC_VER) && _MSC_VER >= 1900))
+// DEPRECATED: Do not key off LANG_CXX11. Instead, write more accurate condition
+// that checks whether the C++ feature you need is available or missing, and
+// define a more specific feature macro (GOOGLE_HAVE_FEATURE_FOO). You can check
+// http://en.cppreference.com/w/cpp/compiler_support for compiler support on C++
+// features.
+// Define this to 1 if the code is compiled in C++11 mode; leave it
+// undefined otherwise. Do NOT define it to 0 -- that causes
+// '#ifdef LANG_CXX11' to behave differently from '#if LANG_CXX11'.
+#define LANG_CXX11 1
+#endif
+
+// This sanity check can be removed when all references to
+// LANG_CXX11 is removed from the code base.
+#if defined(__cplusplus) && !defined(LANG_CXX11) && !defined(SWIG)
+#error "LANG_CXX11 is required."
+#endif
+
+// GOOGLE_OBSCURE_SIGNAL
+#if defined(__APPLE__)
+// No SIGPWR on MacOSX. SIGINFO seems suitably obscure.
+#define GOOGLE_OBSCURE_SIGNAL SIGINFO
+#else
+/* We use SIGPWR since that seems unlikely to be used for other reasons. */
+#define GOOGLE_OBSCURE_SIGNAL SIGPWR
+#endif
+
+// ABSL_FUNC_PTR_TO_CHAR_PTR
+// On some platforms, a "function pointer" points to a function descriptor
+// rather than directly to the function itself.
+// Use ABSL_FUNC_PTR_TO_CHAR_PTR(func) to get a char-pointer to the first
+// instruction of the function func.
+// TODO(b/30407660): Move this macro into Abseil when symbolizer is released in
+// Abseil.
+#if defined(__cplusplus)
+#if (defined(__powerpc__) && !(_CALL_ELF > 1)) || defined(__ia64)
+// use opd section for function descriptors on these platforms, the function
+// address is the first word of the descriptor
+namespace absl {
+enum { kPlatformUsesOPDSections = 1 };
+} // namespace absl
+#define ABSL_FUNC_PTR_TO_CHAR_PTR(func) (reinterpret_cast<char **>(func)[0])
+#else // not PPC or IA64
+namespace absl {
+enum { kPlatformUsesOPDSections = 0 };
+} // namespace absl
+#define ABSL_FUNC_PTR_TO_CHAR_PTR(func) (reinterpret_cast<char *>(func))
+#endif // PPC or IA64
+#endif // __cplusplus
+
+// -----------------------------------------------------------------------------
+// Utility Functions
+// -----------------------------------------------------------------------------
+
+// sized_delete
+#ifdef __cplusplus
+namespace base {
+// We support C++14's sized deallocation for all C++ builds,
+// though for other toolchains, we fall back to using delete.
+inline void sized_delete(void *ptr, size_t size) {
+#ifdef GOOGLE_HAVE_SIZED_DELETE
+ ::operator delete(ptr, size);
+#else
+ (void)size;
+ ::operator delete(ptr);
+#endif // GOOGLE_HAVE_SIZED_DELETE
+}
+
+inline void sized_delete_array(void *ptr, size_t size) {
+#ifdef GOOGLE_HAVE_SIZED_DELETEARRAY
+ ::operator delete[](ptr, size);
+#else
+ (void) size;
+ ::operator delete[](ptr);
+#endif
+}
+} // namespace base
+#endif // __cplusplus
+
+// -----------------------------------------------------------------------------
+// Endianness
+// -----------------------------------------------------------------------------
+
+// IS_LITTLE_ENDIAN, IS_BIG_ENDIAN
+
+// Allow compiler -D defines to override detection here
+// which occasionally fails (e.g., on CRAN Solaris)
+#if defined(IS_LITTLE_ENDIAN)
+#undef IS_BIG_ENDIAN
+#elif defined(IS_BIG_ENDIAN)
+#undef IS_LITTLE_ENDIAN
+#else
+
+#if defined __linux__ || defined OS_ANDROID || defined(__ANDROID__)
+// TODO(user): http://b/21460321; use one of OS_ANDROID or __ANDROID__.
+// _BIG_ENDIAN
+#include <endian.h>
+
+#elif defined(__APPLE__)
+
+// BIG_ENDIAN
+#include <machine/endian.h> // NOLINT(build/include)
+/* Let's try and follow the Linux convention */
+#define __BYTE_ORDER BYTE_ORDER
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#define __BIG_ENDIAN BIG_ENDIAN
+
+#endif
+
+// defines __BYTE_ORDER for MSVC
+#ifdef _MSC_VER
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#define IS_LITTLE_ENDIAN
+#else
+
+// define the macros IS_LITTLE_ENDIAN or IS_BIG_ENDIAN
+// using the above endian definitions from endian.h if
+// endian.h was included
+#ifdef __BYTE_ORDER
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define IS_LITTLE_ENDIAN
+#endif
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define IS_BIG_ENDIAN
+#endif
+
+#else // __BYTE_ORDER
+
+#if defined(__LITTLE_ENDIAN__)
+#define IS_LITTLE_ENDIAN
+#elif defined(__BIG_ENDIAN__)
+#define IS_BIG_ENDIAN
+#endif
+
+#endif // __BYTE_ORDER
+#endif // _MSC_VER
+#endif // #if defined(IS_LITTLE_ENDIAN) ... #else
+
+// byte swap functions (bswap_16, bswap_32, bswap_64).
+
+// The following guarantees declaration of the byte swap functions
+#ifdef _MSC_VER
+#include <cstdlib> // NOLINT(build/include)
+#define bswap_16(x) _byteswap_ushort(x)
+#define bswap_32(x) _byteswap_ulong(x)
+#define bswap_64(x) _byteswap_uint64(x)
+
+#elif defined(__APPLE__)
+// Mac OS X / Darwin features
+#include <libkern/OSByteOrder.h>
+#define bswap_16(x) OSSwapInt16(x)
+#define bswap_32(x) OSSwapInt32(x)
+#define bswap_64(x) OSSwapInt64(x)
+
+#elif defined(__GLIBC__) || defined(__BIONIC__) || defined(__ASYLO__)
+#include <byteswap.h> // IWYU pragma: export
+
+#else
+
+static inline uint16 bswap_16(uint16 x) {
+#ifdef __cplusplus
+ return static_cast<uint16>(((x & 0xFF) << 8) | ((x & 0xFF00) >> 8));
+#else
+ return (uint16)(((x & 0xFF) << 8) | ((x & 0xFF00) >> 8)); // NOLINT
+#endif // __cplusplus
+}
+#define bswap_16(x) bswap_16(x)
+static inline uint32 bswap_32(uint32 x) {
+ return (((x & 0xFF) << 24) |
+ ((x & 0xFF00) << 8) |
+ ((x & 0xFF0000) >> 8) |
+ ((x & 0xFF000000) >> 24));
+}
+#define bswap_32(x) bswap_32(x)
+static inline uint64 bswap_64(uint64 x) {
+ return (((x & 0xFFULL) << 56) |
+ ((x & 0xFF00ULL) << 40) |
+ ((x & 0xFF0000ULL) << 24) |
+ ((x & 0xFF000000ULL) << 8) |
+ ((x & 0xFF00000000ULL) >> 8) |
+ ((x & 0xFF0000000000ULL) >> 24) |
+ ((x & 0xFF000000000000ULL) >> 40) |
+ ((x & 0xFF00000000000000ULL) >> 56));
+}
+#define bswap_64(x) bswap_64(x)
+
+#endif
+
+// -----------------------------------------------------------------------------
+// Hash
+// -----------------------------------------------------------------------------
+
+#ifdef __cplusplus
+#ifdef STL_MSVC // not always the same as _MSC_VER
+#include "s2/third_party/absl/base/internal/port_hash.inc"
+#else
+struct PortableHashBase {};
+#endif // STL_MSVC
+#endif // __cplusplus
+
+// -----------------------------------------------------------------------------
+// Global Variables
+// -----------------------------------------------------------------------------
+
+// PATH_SEPARATOR
+// Define the OS's path separator
+//
+// NOTE: Assuming the path separator at compile time is discouraged.
+// Prefer instead to be tolerant of both possible separators whenever possible.
+#ifdef __cplusplus // C won't merge duplicate const variables at link time
+// Some headers provide a macro for this (GCC's system.h), remove it so that we
+// can use our own.
+#undef PATH_SEPARATOR
+#if defined(_WIN32)
+const char PATH_SEPARATOR = '\\';
+#else
+const char PATH_SEPARATOR = '/';
+#endif // _WIN32
+#endif // __cplusplus
+
+// -----------------------------------------------------------------------------
+// Type Alias
+// -----------------------------------------------------------------------------
+
+// uint, ushort, ulong
+#if defined __linux__
+// The uint mess:
+// mysql.h sets _GNU_SOURCE which sets __USE_MISC in <features.h>
+// sys/types.h typedefs uint if __USE_MISC
+// mysql typedefs uint if HAVE_UINT not set
+// The following typedef is carefully considered, and should not cause
+// any clashes
+#if !defined(__USE_MISC)
+#if !defined(HAVE_UINT)
+#define HAVE_UINT 1
+typedef unsigned int uint;
+#endif // !HAVE_UINT
+#if !defined(HAVE_USHORT)
+#define HAVE_USHORT 1
+typedef unsigned short ushort; // NOLINT
+#endif // !HAVE_USHORT
+#if !defined(HAVE_ULONG)
+#define HAVE_ULONG 1
+typedef unsigned long ulong; // NOLINT
+#endif // !HAVE_ULONG
+#endif // !__USE_MISC
+
+#endif // __linux__
+
+#ifdef _MSC_VER /* if Visual C++ */
+// VC++ doesn't understand "uint"
+#ifndef HAVE_UINT
+#define HAVE_UINT 1
+typedef unsigned int uint;
+#endif // !HAVE_UINT
+#endif // _MSC_VER
+
+#ifdef _MSC_VER
+// uid_t
+// MSVC doesn't have uid_t
+typedef int uid_t;
+
+// pid_t
+// Defined all over the place.
+typedef int pid_t;
+#endif // _MSC_VER
+
+// mode_t
+#ifdef _MSC_VER
+// From stat.h
+typedef unsigned int mode_t;
+#endif // _MSC_VER
+
+// sig_t
+#ifdef _MSC_VER
+typedef void (*sig_t)(int);
+#endif // _MSC_VER
+
+// u_int16_t, int16_t
+#ifdef _MSC_VER
+// u_int16_t, int16_t don't exist in MSVC
+typedef unsigned short u_int16_t; // NOLINT
+typedef short int16_t; // NOLINT
+#endif // _MSC_VER
+
+// using std::hash
+#ifdef _MSC_VER
+#ifdef __cplusplus
+// Define a minimal set of things typically available in the global
+// namespace in Google code. ::string is handled elsewhere, and uniformly
+// for all targets.
+#include <functional>
+using std::hash;
+#endif // __cplusplus
+#endif // _MSC_VER
+
+// printf macros
+// __STDC_FORMAT_MACROS must be defined before inttypes.h inclusion */
+#if defined(__APPLE__)
+/* From MacOSX's inttypes.h:
+ * "C++ implementations should define these macros only when
+ * __STDC_FORMAT_MACROS is defined before <inttypes.h> is included." */
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS
+#endif /* __STDC_FORMAT_MACROS */
+#endif /* __APPLE__ */
+
+// printf macros for size_t, in the style of inttypes.h
+#if defined(_LP64) || defined(__APPLE__)
+#define __PRIS_PREFIX "z"
+#else
+#define __PRIS_PREFIX
+#endif
+
+// Use these macros after a % in a printf format string
+// to get correct 32/64 bit behavior, like this:
+// size_t size = records.size();
+// printf("%" PRIuS "\n", size);
+#define PRIdS __PRIS_PREFIX "d"
+#define PRIxS __PRIS_PREFIX "x"
+#define PRIuS __PRIS_PREFIX "u"
+#define PRIXS __PRIS_PREFIX "X"
+#define PRIoS __PRIS_PREFIX "o"
+
+#define GPRIuPTHREAD "lu"
+#define GPRIxPTHREAD "lx"
+#if defined(__APPLE__)
+#define PRINTABLE_PTHREAD(pthreadt) reinterpret_cast<uintptr_t>(pthreadt)
+#else
+#define PRINTABLE_PTHREAD(pthreadt) pthreadt
+#endif
+
+#ifdef PTHREADS_REDHAT_WIN32
+#include <pthread.h> // NOLINT(build/include)
+#include <iosfwd> // NOLINT(build/include)
+// pthread_t is not a simple integer or pointer on Win32
+std::ostream &operator<<(std::ostream &out, const pthread_t &thread_id);
+#endif
+
+// -----------------------------------------------------------------------------
+// Predefined System/Language Macros
+// -----------------------------------------------------------------------------
+
+// EXFULL
+#if defined(__APPLE__)
+// Linux has this in <linux/errno.h>
+#define EXFULL ENOMEM // not really that great a translation...
+#endif // __APPLE__
+#ifdef _MSC_VER
+// This actually belongs in errno.h but there's a name conflict in errno
+// on WinNT. They (and a ton more) are also found in Winsock2.h, but
+// if'd out under NT. We need this subset at minimum.
+#define EXFULL ENOMEM // not really that great a translation...
+#endif // _MSC_VER
+
+// MSG_NOSIGNAL
+#if defined(__APPLE__)
+// Doesn't exist on OSX.
+#define MSG_NOSIGNAL 0
+#endif // __APPLE__
+
+// __ptr_t
+#if defined(__APPLE__)
+// Linux has this in <sys/cdefs.h>
+#define __ptr_t void *
+#endif // __APPLE__
+#ifdef _MSC_VER
+// From glob.h
+#define __ptr_t void *
+#endif
+
+// HUGE_VALF
+#ifdef _MSC_VER
+#include <cmath> // for HUGE_VAL
+
+#ifndef HUGE_VALF
+#define HUGE_VALF (static_cast<float>(HUGE_VAL))
+#endif
+#endif // _MSC_VER
+
+// MAP_ANONYMOUS
+#if defined(__APPLE__)
+// For mmap, Linux defines both MAP_ANONYMOUS and MAP_ANON and says MAP_ANON is
+// deprecated. In Darwin, MAP_ANON is all there is.
+#if !defined MAP_ANONYMOUS
+#define MAP_ANONYMOUS MAP_ANON
+#endif // !MAP_ANONYMOUS
+#endif // __APPLE__
+
+// PATH_MAX
+// You say tomato, I say atotom
+#ifdef _MSC_VER
+#define PATH_MAX MAX_PATH
+#endif
+
+// -----------------------------------------------------------------------------
+// Predefined System/Language Functions
+// -----------------------------------------------------------------------------
+
+// strtoq, strtouq, atoll
+#ifdef _MSC_VER
+#define strtoq _strtoi64
+#define strtouq _strtoui64
+#define atoll _atoi64
+#endif // _MSC_VER
+
+#ifdef _MSC_VER
+// You say tomato, I say _tomato
+#define strcasecmp _stricmp
+#define strncasecmp _strnicmp
+#define strdup _strdup
+#define tempnam _tempnam
+#define chdir _chdir
+#define getpid _getpid
+#define getcwd _getcwd
+#define putenv _putenv
+#define timezone _timezone
+#define tzname _tzname
+#endif // _MSC_VER
+
+// random, srandom
+#ifdef _MSC_VER
+// You say tomato, I say toma
+inline int random() { return rand(); }
+inline void srandom(unsigned int seed) { srand(seed); }
+#endif // _MSC_VER
+
+// bcopy, bzero
+#ifdef _MSC_VER
+// You say juxtapose, I say transpose
+#define bcopy(s, d, n) memcpy(d, s, n)
+// Really from <string.h>
+inline void bzero(void *s, int n) { memset(s, 0, n); }
+#endif // _MSC_VER
+
+// gethostbyname
+#if defined(_WIN32) || defined(__APPLE__)
+// gethostbyname() *is* thread-safe for Windows native threads. It is also
+// safe on Mac OS X and iOS, where it uses thread-local storage, even though the
+// manpages claim otherwise. For details, see
+// http://lists.apple.com/archives/Darwin-dev/2006/May/msg00008.html
+#else
+// gethostbyname() is not thread-safe. So disallow its use. People
+// should either use the HostLookup::Lookup*() methods, or gethostbyname_r()
+#define gethostbyname gethostbyname_is_not_thread_safe_DO_NOT_USE
+#endif
+
+// -----------------------------------------------------------------------------
+// Performance Optimization
+// -----------------------------------------------------------------------------
+
+// Alignment
+
+// Unaligned APIs
+
+// Portable handling of unaligned loads, stores, and copies.
+// On some platforms, like ARM, the copy functions can be more efficient
+// then a load and a store.
+//
+// It is possible to implement all of these these using constant-length memcpy
+// calls, which is portable and will usually be inlined into simple loads and
+// stores if the architecture supports it. However, such inlining usually
+// happens in a pass that's quite late in compilation, which means the resulting
+// loads and stores cannot participate in many other optimizations, leading to
+// overall worse code.
+// TODO(user): These APIs are forked in Abseil, see
+// LLVM, we should reimplement these APIs with functions calling memcpy(), and
+// maybe publish them in Abseil.
+
+// The unaligned API is C++ only. The declarations use C++ features
+// (namespaces, inline) which are absent or incompatible in C.
+#if defined(__cplusplus)
+
+#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) || \
+ defined(MEMORY_SANITIZER)
+// Consider we have an unaligned load/store of 4 bytes from address 0x...05.
+// AddressSanitizer will treat it as a 3-byte access to the range 05:07 and
+// will miss a bug if 08 is the first unaddressable byte.
+// ThreadSanitizer will also treat this as a 3-byte access to 05:07 and will
+// miss a race between this access and some other accesses to 08.
+// MemorySanitizer will correctly propagate the shadow on unaligned stores
+// and correctly report bugs on unaligned loads, but it may not properly
+// update and report the origin of the uninitialized memory.
+// For all three tools, replacing an unaligned access with a tool-specific
+// callback solves the problem.
+
+// Make sure uint16_t/uint32_t/uint64_t are defined.
+#include <cstdint>
+
+extern "C" {
+uint16_t __sanitizer_unaligned_load16(const void *p);
+uint32_t __sanitizer_unaligned_load32(const void *p);
+uint64_t __sanitizer_unaligned_load64(const void *p);
+void __sanitizer_unaligned_store16(void *p, uint16_t v);
+void __sanitizer_unaligned_store32(void *p, uint32_t v);
+void __sanitizer_unaligned_store64(void *p, uint64_t v);
+} // extern "C"
+
+inline uint16 UNALIGNED_LOAD16(const void *p) {
+ return __sanitizer_unaligned_load16(p);
+}
+
+inline uint32 UNALIGNED_LOAD32(const void *p) {
+ return __sanitizer_unaligned_load32(p);
+}
+
+inline uint64 UNALIGNED_LOAD64(const void *p) {
+ return __sanitizer_unaligned_load64(p);
+}
+
+inline void UNALIGNED_STORE16(void *p, uint16 v) {
+ __sanitizer_unaligned_store16(p, v);
+}
+
+inline void UNALIGNED_STORE32(void *p, uint32 v) {
+ __sanitizer_unaligned_store32(p, v);
+}
+
+inline void UNALIGNED_STORE64(void *p, uint64 v) {
+ __sanitizer_unaligned_store64(p, v);
+}
+
+#elif defined(UNDEFINED_BEHAVIOR_SANITIZER)
+
+inline uint16 UNALIGNED_LOAD16(const void *p) {
+ uint16 t;
+ memcpy(&t, p, sizeof t);
+ return t;
+}
+
+inline uint32 UNALIGNED_LOAD32(const void *p) {
+ uint32 t;
+ memcpy(&t, p, sizeof t);
+ return t;
+}
+
+inline uint64 UNALIGNED_LOAD64(const void *p) {
+ uint64 t;
+ memcpy(&t, p, sizeof t);
+ return t;
+}
+
+inline void UNALIGNED_STORE16(void *p, uint16 v) { memcpy(p, &v, sizeof v); }
+
+inline void UNALIGNED_STORE32(void *p, uint32 v) { memcpy(p, &v, sizeof v); }
+
+inline void UNALIGNED_STORE64(void *p, uint64 v) { memcpy(p, &v, sizeof v); }
+
+#elif defined(__x86_64__) || defined(_M_X64) || defined(__i386) || \
+ defined(_M_IX86) || defined(__ppc__) || defined(__PPC__) || \
+ defined(__ppc64__) || defined(__PPC64__)
+
+// x86 and x86-64 can perform unaligned loads/stores directly;
+// modern PowerPC hardware can also do unaligned integer loads and stores;
+// but note: the FPU still sends unaligned loads and stores to a trap handler!
+
+#define UNALIGNED_LOAD16(_p) (*reinterpret_cast<const uint16 *>(_p))
+#define UNALIGNED_LOAD32(_p) (*reinterpret_cast<const uint32 *>(_p))
+#define UNALIGNED_LOAD64(_p) (*reinterpret_cast<const uint64 *>(_p))
+
+#define UNALIGNED_STORE16(_p, _val) (*reinterpret_cast<uint16 *>(_p) = (_val))
+#define UNALIGNED_STORE32(_p, _val) (*reinterpret_cast<uint32 *>(_p) = (_val))
+#define UNALIGNED_STORE64(_p, _val) (*reinterpret_cast<uint64 *>(_p) = (_val))
+
+#elif defined(__arm__) && !defined(__ARM_ARCH_5__) && \
+ !defined(__ARM_ARCH_5T__) && !defined(__ARM_ARCH_5TE__) && \
+ !defined(__ARM_ARCH_5TEJ__) && !defined(__ARM_ARCH_6__) && \
+ !defined(__ARM_ARCH_6J__) && !defined(__ARM_ARCH_6K__) && \
+ !defined(__ARM_ARCH_6Z__) && !defined(__ARM_ARCH_6ZK__) && \
+ !defined(__ARM_ARCH_6T2__)
+
+// ARMv7 and newer support native unaligned accesses, but only of 16-bit
+// and 32-bit values (not 64-bit); older versions either raise a fatal signal,
+// do an unaligned read and rotate the words around a bit, or do the reads very
+// slowly (trip through kernel mode). There's no simple #define that says just
+// “ARMv7 or higher”, so we have to filter away all ARMv5 and ARMv6
+// sub-architectures. Newer gcc (>= 4.6) set an __ARM_FEATURE_ALIGNED #define,
+// so in time, maybe we can move on to that.
+//
+// This is a mess, but there's not much we can do about it.
+//
+// To further complicate matters, only LDR instructions (single reads) are
+// allowed to be unaligned, not LDRD (two reads) or LDM (many reads). Unless we
+// explicitly tell the compiler that these accesses can be unaligned, it can and
+// will combine accesses. On armcc, the way to signal this is done by accessing
+// through the type (uint32 __packed *), but GCC has no such attribute
+// (it ignores __attribute__((packed)) on individual variables). However,
+// we can tell it that a _struct_ is unaligned, which has the same effect,
+// so we do that.
+
+namespace base {
+namespace internal {
+
+struct Unaligned16Struct {
+ uint16 value;
+ uint8 dummy; // To make the size non-power-of-two.
+} ABSL_ATTRIBUTE_PACKED;
+
+struct Unaligned32Struct {
+ uint32 value;
+ uint8 dummy; // To make the size non-power-of-two.
+} ABSL_ATTRIBUTE_PACKED;
+
+} // namespace internal
+} // namespace base
+
+#define UNALIGNED_LOAD16(_p) \
+ ((reinterpret_cast<const ::base::internal::Unaligned16Struct *>(_p))->value)
+#define UNALIGNED_LOAD32(_p) \
+ ((reinterpret_cast<const ::base::internal::Unaligned32Struct *>(_p))->value)
+
+#define UNALIGNED_STORE16(_p, _val) \
+ ((reinterpret_cast< ::base::internal::Unaligned16Struct *>(_p))->value = \
+ (_val))
+#define UNALIGNED_STORE32(_p, _val) \
+ ((reinterpret_cast< ::base::internal::Unaligned32Struct *>(_p))->value = \
+ (_val))
+
+// TODO(user): NEON supports unaligned 64-bit loads and stores.
+// See if that would be more efficient on platforms supporting it,
+// at least for copies.
+
+inline uint64 UNALIGNED_LOAD64(const void *p) {
+ uint64 t;
+ memcpy(&t, p, sizeof t);
+ return t;
+}
+
+inline void UNALIGNED_STORE64(void *p, uint64 v) { memcpy(p, &v, sizeof v); }
+
+#else
+
+#define NEED_ALIGNED_LOADS
+
+// These functions are provided for architectures that don't support
+// unaligned loads and stores.
+
+inline uint16 UNALIGNED_LOAD16(const void *p) {
+ uint16 t;
+ memcpy(&t, p, sizeof t);
+ return t;
+}
+
+inline uint32 UNALIGNED_LOAD32(const void *p) {
+ uint32 t;
+ memcpy(&t, p, sizeof t);
+ return t;
+}
+
+inline uint64 UNALIGNED_LOAD64(const void *p) {
+ uint64 t;
+ memcpy(&t, p, sizeof t);
+ return t;
+}
+
+inline void UNALIGNED_STORE16(void *p, uint16 v) { memcpy(p, &v, sizeof v); }
+
+inline void UNALIGNED_STORE32(void *p, uint32 v) { memcpy(p, &v, sizeof v); }
+
+inline void UNALIGNED_STORE64(void *p, uint64 v) { memcpy(p, &v, sizeof v); }
+
+#endif
+
+// The UNALIGNED_LOADW and UNALIGNED_STOREW macros load and store values
+// of type uword_t.
+#ifdef _LP64
+#define UNALIGNED_LOADW(_p) UNALIGNED_LOAD64(_p)
+#define UNALIGNED_STOREW(_p, _val) UNALIGNED_STORE64(_p, _val)
+#else
+#define UNALIGNED_LOADW(_p) UNALIGNED_LOAD32(_p)
+#define UNALIGNED_STOREW(_p, _val) UNALIGNED_STORE32(_p, _val)
+#endif
+
+inline void UnalignedCopy16(const void *src, void *dst) {
+ UNALIGNED_STORE16(dst, UNALIGNED_LOAD16(src));
+}
+
+inline void UnalignedCopy32(const void *src, void *dst) {
+ UNALIGNED_STORE32(dst, UNALIGNED_LOAD32(src));
+}
+
+inline void UnalignedCopy64(const void *src, void *dst) {
+ if (sizeof(void *) == 8) {
+ UNALIGNED_STORE64(dst, UNALIGNED_LOAD64(src));
+ } else {
+ const char *src_char = reinterpret_cast<const char *>(src);
+ char *dst_char = reinterpret_cast<char *>(dst);
+
+ UNALIGNED_STORE32(dst_char, UNALIGNED_LOAD32(src_char));
+ UNALIGNED_STORE32(dst_char + 4, UNALIGNED_LOAD32(src_char + 4));
+ }
+}
+
+#endif // defined(__cplusplus), end of unaligned API
+
+// aligned_malloc, aligned_free
+#if defined(__ANDROID__) || defined(__ASYLO__) || defined(_WIN32)
+#include <malloc.h> // for memalign()
+#endif
+
+// __ASYLO__ platform uses newlib without an underlying OS, which provides
+// memalign, but not posix_memalign.
+#if defined(__cplusplus) && \
+ (((defined(__GNUC__) || defined(__APPLE__) || \
+ defined(__NVCC__)) && \
+ !defined(SWIG)) || \
+ ((__GNUC__ >= 3 || defined(__clang__)) && defined(__ANDROID__)) || \
+ defined(__ASYLO__))
+inline void *aligned_malloc(size_t size, size_t minimum_alignment) {
+#if defined(__ANDROID__) || defined(OS_ANDROID) || defined(__ASYLO__) || defined(_WIN32) || defined(__sun) || defined(sun)
+# if defined(_WIN32)
+ return _aligned_malloc(size, minimum_alignment);
+# else
+ return memalign(minimum_alignment, size);
+# endif
+#else // !__ANDROID__ && !OS_ANDROID && !__ASYLO__
+ // posix_memalign requires that the requested alignment be at least
+ // sizeof(void*). In this case, fall back on malloc which should return memory
+ // aligned to at least the size of a pointer.
+ const size_t required_alignment = sizeof(void*);
+ if (minimum_alignment < required_alignment)
+ return malloc(size);
+ void *ptr = nullptr;
+ if (posix_memalign(&ptr, minimum_alignment, size) == 0)
+ return ptr;
+ return nullptr;
+#endif
+}
+
+inline void aligned_free(void *aligned_memory) {
+ free(aligned_memory);
+}
+
+#elif defined(_MSC_VER) // MSVC
+
+inline void *aligned_malloc(size_t size, size_t minimum_alignment) {
+ return _aligned_malloc(size, minimum_alignment);
+}
+
+inline void aligned_free(void *aligned_memory) {
+ _aligned_free(aligned_memory);
+}
+
+#endif // aligned_malloc, aligned_free
+
+// ALIGNED_CHAR_ARRAY
+//
+// Provides a char array with the exact same alignment as another type. The
+// first parameter must be a complete type, the second parameter is how many
+// of that type to provide space for.
+//
+// ALIGNED_CHAR_ARRAY(struct stat, 16) storage_;
+//
+#if defined(__cplusplus)
+#undef ALIGNED_CHAR_ARRAY
+// Because MSVC and older GCCs require that the argument to their alignment
+// construct to be a literal constant integer, we use a template instantiated
+// at all the possible powers of two.
+#ifndef SWIG
+template<int alignment, int size> struct AlignType { };
+template<int size> struct AlignType<0, size> { typedef char result[size]; };
+#if defined(_MSC_VER)
+#define BASE_PORT_H_ALIGN_ATTRIBUTE(X) __declspec(align(X))
+#define BASE_PORT_H_ALIGN_OF(T) __alignof(T)
+#elif defined(__GNUC__) || defined(__INTEL_COMPILER)
+#define BASE_PORT_H_ALIGN_ATTRIBUTE(X) __attribute__((aligned(X)))
+#define BASE_PORT_H_ALIGN_OF(T) __alignof__(T)
+#endif
+
+#if defined(BASE_PORT_H_ALIGN_ATTRIBUTE)
+
+#define BASE_PORT_H_ALIGNTYPE_TEMPLATE(X) \
+ template<int size> struct AlignType<X, size> { \
+ typedef BASE_PORT_H_ALIGN_ATTRIBUTE(X) char result[size]; \
+ }
+
+BASE_PORT_H_ALIGNTYPE_TEMPLATE(1);
+BASE_PORT_H_ALIGNTYPE_TEMPLATE(2);
+BASE_PORT_H_ALIGNTYPE_TEMPLATE(4);
+BASE_PORT_H_ALIGNTYPE_TEMPLATE(8);
+BASE_PORT_H_ALIGNTYPE_TEMPLATE(16);
+BASE_PORT_H_ALIGNTYPE_TEMPLATE(32);
+BASE_PORT_H_ALIGNTYPE_TEMPLATE(64);
+BASE_PORT_H_ALIGNTYPE_TEMPLATE(128);
+BASE_PORT_H_ALIGNTYPE_TEMPLATE(256);
+BASE_PORT_H_ALIGNTYPE_TEMPLATE(512);
+BASE_PORT_H_ALIGNTYPE_TEMPLATE(1024);
+BASE_PORT_H_ALIGNTYPE_TEMPLATE(2048);
+BASE_PORT_H_ALIGNTYPE_TEMPLATE(4096);
+BASE_PORT_H_ALIGNTYPE_TEMPLATE(8192);
+// Any larger and MSVC++ will complain.
+
+#define ALIGNED_CHAR_ARRAY(T, Size) \
+ typename AlignType<BASE_PORT_H_ALIGN_OF(T), sizeof(T) * Size>::result
+
+#undef BASE_PORT_H_ALIGNTYPE_TEMPLATE
+#undef BASE_PORT_H_ALIGN_ATTRIBUTE
+
+#else // defined(BASE_PORT_H_ALIGN_ATTRIBUTE)
+#define ALIGNED_CHAR_ARRAY \
+ you_must_define_ALIGNED_CHAR_ARRAY_for_your_compiler_in_base_port_h
+#endif // defined(BASE_PORT_H_ALIGN_ATTRIBUTE)
+
+#else // !SWIG
+
+// SWIG can't represent alignment and doesn't care about alignment on data
+// members (it works fine without it).
+template<typename Size>
+struct AlignType { typedef char result[Size]; };
+#define ALIGNED_CHAR_ARRAY(T, Size) AlignType<Size * sizeof(T)>::result
+
+// Enough to parse with SWIG, will never be used by running code.
+#define BASE_PORT_H_ALIGN_OF(Type) 16
+
+#endif // !SWIG
+#else // __cplusplus
+#define ALIGNED_CHAR_ARRAY ALIGNED_CHAR_ARRAY_is_not_available_without_Cplusplus
+#endif // __cplusplus
+
+#endif // S2_BASE_PORT_H_
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef S2_BASE_SPINLOCK_H_
+#define S2_BASE_SPINLOCK_H_
+
+#include <atomic>
+
+
+class SpinLock {
+ public:
+ SpinLock() = default;
+ ~SpinLock() = default;
+ SpinLock(SpinLock const&) = delete;
+ SpinLock& operator=(SpinLock const&) = delete;
+
+ inline void Lock() {
+ while (locked_.exchange(true, std::memory_order_acquire)) {
+ // Spin.
+ continue;
+ }
+ }
+
+ inline void Unlock() {
+ locked_.store(false, std::memory_order_release);
+ }
+
+ inline bool IsHeld() const {
+ return locked_.load(std::memory_order_relaxed);
+ }
+
+ private:
+ std::atomic_bool locked_{false};
+};
+
+class SpinLockHolder {
+ public:
+ inline explicit SpinLockHolder(SpinLock* l) : lock_(l) { lock_->Lock(); }
+ inline ~SpinLockHolder() { lock_->Unlock(); }
+
+ SpinLockHolder(const SpinLockHolder&) = delete;
+ SpinLockHolder& operator=(const SpinLockHolder&) = delete;
+
+ private:
+ SpinLock* lock_;
+};
+
+#endif // S2_BASE_SPINLOCK_H_
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// NOTE: See third_party/absl/strings for more options.
+//
+// As of 2017q4, most use of these routines is considered legacy: use
+// of absl::StrCat, absl::Substitute, or absl::StrFormat is preferred for
+// performance and safety reasons.
+
+#ifndef S2_BASE_STRINGPRINTF_H_
+#define S2_BASE_STRINGPRINTF_H_
+
+#include <cstdarg>
+#include <string>
+#include <vector>
+
+#include "s2/base/port.h"
+
+// Return a C++ string
+extern string StringPrintf(const char* format, ...)
+ // Tell the compiler to do printf format string checking.
+ ABSL_PRINTF_ATTRIBUTE(1, 2);
+
+// Store result into a supplied string and return it
+extern const string& SStringPrintf(string* dst, const char* format, ...)
+ // Tell the compiler to do printf format string checking.
+ ABSL_PRINTF_ATTRIBUTE(2, 3);
+
+// Append result to a supplied string
+extern void StringAppendF(string* dst, const char* format, ...)
+ // Tell the compiler to do printf format string checking.
+ ABSL_PRINTF_ATTRIBUTE(2, 3);
+
+// Lower-level routine that takes a va_list and appends to a specified
+// string. All other routines are just convenience wrappers around it.
+//
+// Implementation note: the va_list is never modified, this implementation
+// always operates on copies.
+extern void StringAppendV(string* dst, const char* format, va_list ap);
+
+#endif // S2_BASE_STRINGPRINTF_H_
--- /dev/null
+// Copyright 2008 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+//
+// Architecture-neutral plug compatible replacements for strtol() friends.
+//
+// Long's have different lengths on ILP-32 and LP-64 platforms, and so overflow
+// behavior across the two varies when strtol() and similar are used to parse
+// 32-bit integers. Similar problems exist with atoi(), because although it
+// has an all-integer interface, it uses strtol() internally, and so suffers
+// from the same narrowing problems on assignments to int.
+//
+// Examples:
+// errno = 0;
+// i = strtol("3147483647", nullptr, 10);
+// printf("%d, errno %d\n", i, errno);
+// // 32-bit platform: 2147483647, errno 34
+// // 64-bit platform: -1147483649, errno 0
+//
+// printf("%d\n", atoi("3147483647"));
+// // 32-bit platform: 2147483647
+// // 64-bit platform: -1147483649
+//
+// A way round this is to define local replacements for these, and use them
+// instead of the standard libc functions.
+//
+// In most 32-bit cases the replacements can be inlined away to a call to the
+// libc function. In a couple of 64-bit cases, however, adapters are required,
+// to provide the right overflow and errno behavior.
+//
+
+#ifndef S2_BASE_STRTOINT_H_
+#define S2_BASE_STRTOINT_H_
+
+#include <cstdlib> // For strtol* functions.
+#include <string>
+#include "s2/base/integral_types.h"
+#include "s2/base/port.h"
+#include "s2/third_party/absl/base/macros.h"
+
+// Adapter functions for handling overflow and errno.
+int32 strto32_adapter(const char *nptr, char **endptr, int base);
+uint32 strtou32_adapter(const char *nptr, char **endptr, int base);
+
+// Conversions to a 32-bit integer can pass the call to strto[u]l on 32-bit
+// platforms, but need a little extra work on 64-bit platforms.
+inline int32 strto32(const char *nptr, char **endptr, int base) {
+ if (sizeof(int32) == sizeof(long))
+ return static_cast<int32>(strtol(nptr, endptr, base));
+ else
+ return strto32_adapter(nptr, endptr, base);
+}
+
+inline uint32 strtou32(const char *nptr, char **endptr, int base) {
+ if (sizeof(uint32) == sizeof(unsigned long))
+ return static_cast<uint32>(strtoul(nptr, endptr, base));
+ else
+ return strtou32_adapter(nptr, endptr, base);
+}
+
+// For now, long long is 64-bit on all the platforms we care about, so these
+// functions can simply pass the call to strto[u]ll.
+inline int64 strto64(const char *nptr, char **endptr, int base) {
+ static_assert(sizeof(int64) == sizeof(long long),
+ "sizeof int64 is not sizeof long long");
+ return strtoll(nptr, endptr, base);
+}
+
+inline uint64 strtou64(const char *nptr, char **endptr, int base) {
+ static_assert(sizeof(uint64) == sizeof(unsigned long long),
+ "sizeof uint64 is not sizeof long long");
+ return strtoull(nptr, endptr, base);
+}
+
+// Although it returns an int, atoi() is implemented in terms of strtol, and
+// so has differing overflow and underflow behavior. atol is the same.
+inline int32 atoi32(const char *nptr) {
+ return strto32(nptr, nullptr, 10);
+}
+
+inline int64 atoi64(const char *nptr) {
+ return strto64(nptr, nullptr, 10);
+}
+
+// Convenience versions of the above that take a string argument.
+inline int32 atoi32(const string &s) {
+ return atoi32(s.c_str());
+}
+
+inline int64 atoi64(const string &s) {
+ return atoi64(s.c_str());
+}
+
+#endif // S2_BASE_STRTOINT_H_
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef S2_BASE_TIMER_H_
+#define S2_BASE_TIMER_H_
+
+#include <chrono>
+
+#include "s2/base/integral_types.h"
+
+class CycleTimer {
+ public:
+ CycleTimer() = default;
+
+ void Start() {
+ start_ = Now();
+ }
+
+ int64 GetInMs() const {
+ using msec = std::chrono::milliseconds;
+ return std::chrono::duration_cast<msec>(GetDuration()).count();
+ }
+
+ private:
+ using Clock = std::chrono::high_resolution_clock;
+
+ static Clock::time_point Now() {
+ return Clock::now();
+ }
+
+ Clock::duration GetDuration() const {
+ return Now() - start_;
+ }
+
+ Clock::time_point start_;
+};
+
+#endif // S2_BASE_TIMER_H_
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_ENCODED_S2CELL_ID_VECTOR_H_
+#define S2_ENCODED_S2CELL_ID_VECTOR_H_
+
+#include "s2/third_party/absl/types/span.h"
+#include "s2/encoded_uint_vector.h"
+#include "s2/s2cell_id.h"
+
+namespace s2coding {
+
+// Encodes a vector of S2CellIds in a format that can later be decoded as an
+// EncodedS2CellIdVector. The S2CellIds do not need to be valid.
+//
+// REQUIRES: "encoder" uses the default constructor, so that its buffer
+// can be enlarged as necessary by calling Ensure(int).
+void EncodeS2CellIdVector(absl::Span<const S2CellId> v, Encoder* encoder);
+
+// This class represents an encoded vector of S2CellIds. Values are decoded
+// only when they are accessed. This allows for very fast initialization and
+// no additional memory use beyond the encoded data. The encoded data is not
+// owned by this class; typically it points into a large contiguous buffer
+// that contains other encoded data as well.
+//
+// This is one of several helper classes that allow complex data structures to
+// be initialized from an encoded format in constant time and then decoded on
+// demand. This can be a big performance advantage when only a small part of
+// the data structure is actually used.
+//
+// The S2CellIds do not need to be sorted or at the same level. The
+// implementation is biased towards minimizing decoding times rather than
+// space usage.
+//
+// NOTE: If your S2CellIds represent S2Points that have been snapped to
+// S2CellId centers, then EncodedS2PointVector is both faster and smaller.
+class EncodedS2CellIdVector {
+ public:
+ // Constructs an uninitialized object; requires Init() to be called.
+ EncodedS2CellIdVector() {}
+
+ // Initializes the EncodedS2CellIdVector.
+ //
+ // REQUIRES: The Decoder data buffer must outlive this object.
+ bool Init(Decoder* decoder);
+
+ // Returns the size of the original vector.
+ size_t size() const;
+
+ // Returns the element at the given index.
+ S2CellId operator[](int i) const;
+
+ // Returns the index of the first element x such that (x >= target), or
+ // size() if no such element exists.
+ //
+ // REQUIRES: The vector elements are sorted in non-decreasing order.
+ size_t lower_bound(S2CellId target) const;
+
+ // Decodes and returns the entire original vector.
+ std::vector<S2CellId> Decode() const;
+
+ private:
+ // Values are decoded as (base_ + (deltas_[i] << shift_)).
+ EncodedUintVector<uint64> deltas_;
+ uint64 base_;
+ uint8 shift_;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline size_t EncodedS2CellIdVector::size() const {
+ return deltas_.size();
+}
+
+inline S2CellId EncodedS2CellIdVector::operator[](int i) const {
+ return S2CellId((deltas_[i] << shift_) + base_);
+}
+
+inline size_t EncodedS2CellIdVector::lower_bound(S2CellId target) const {
+ // We optimize the search by converting "target" to the corresponding delta
+ // value and then searching directly in the deltas_ vector.
+ //
+ // To invert operator[], we essentially compute ((target - base_) >> shift_)
+ // except that we also need to round up when shifting. The first two cases
+ // ensure that "target" doesn't wrap around past zero when we do this.
+ if (target.id() <= base_) return 0;
+ if (target >= S2CellId::End(S2CellId::kMaxLevel)) return size();
+ return deltas_.lower_bound(
+ (target.id() - base_ + (1ULL << shift_) - 1) >> shift_);
+}
+
+} // namespace s2coding
+
+#endif // S2_ENCODED_S2CELL_ID_VECTOR_H_
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_ENCODED_S2POINT_VECTOR_H_
+#define S2_ENCODED_S2POINT_VECTOR_H_
+
+#include <atomic>
+#include "s2/third_party/absl/types/span.h"
+#include "s2/encoded_string_vector.h"
+#include "s2/encoded_uint_vector.h"
+#include "s2/s2point.h"
+
+namespace s2coding {
+
+// Controls whether to optimize for speed or size when encoding points. (Note
+// that encoding is always lossless, and that currently compact encodings are
+// only possible when points have been snapped to S2CellId centers.)
+enum class CodingHint : uint8 { FAST, COMPACT };
+
+// Encodes a vector of S2Points in a format that can later be decoded as an
+// EncodedS2PointVector.
+//
+// REQUIRES: "encoder" uses the default constructor, so that its buffer
+// can be enlarged as necessary by calling Ensure(int).
+void EncodeS2PointVector(absl::Span<const S2Point> points, CodingHint hint,
+ Encoder* encoder);
+
+// This class represents an encoded vector of S2Points. Values are decoded
+// only when they are accessed. This allows for very fast initialization and
+// no additional memory use beyond the encoded data. The encoded data is not
+// owned by this class; typically it points into a large contiguous buffer
+// that contains other encoded data as well.
+//
+// This is one of several helper classes that allow complex data structures to
+// be initialized from an encoded format in constant time and then decoded on
+// demand. This can be a big performance advantage when only a small part of
+// the data structure is actually used.
+class EncodedS2PointVector {
+ public:
+ // Constructs an uninitialized object; requires Init() to be called.
+ EncodedS2PointVector() {}
+
+ // Initializes the EncodedS2PointVector.
+ //
+ // REQUIRES: The Decoder data buffer must outlive this object.
+ bool Init(Decoder* decoder);
+
+ // Returns the size of the original vector.
+ size_t size() const;
+
+ // Returns the element at the given index.
+ S2Point operator[](int i) const;
+
+ // Decodes and returns the entire original vector.
+ std::vector<S2Point> Decode() const;
+
+ // TODO(ericv): Consider adding a method that returns an adjacent pair of
+ // points. This would save some decoding overhead.
+
+ private:
+ friend void EncodeS2PointVector(absl::Span<const S2Point>, CodingHint,
+ Encoder*);
+ friend void EncodeS2PointVectorFast(absl::Span<const S2Point>, Encoder*);
+ friend void EncodeS2PointVectorCompact(absl::Span<const S2Point>, Encoder*);
+
+ bool InitUncompressedFormat(Decoder* decoder);
+ bool InitCellIdsFormat(Decoder* decoder);
+ S2Point DecodeCellIdsFormat(int i) const;
+
+ // We use a tagged union to represent multiple formats, as opposed to an
+ // abstract base class or templating. This represents the best compromise
+ // between performance, space, and convenience. Note that the overhead of
+ // checking the tag is trivial and will typically be branch-predicted
+ // perfectly.
+ //
+ // TODO(ericv): Once additional formats have been implemented, consider
+ // using std::variant<> instead. It's unclear whether this would have
+ // better or worse performance than the current approach.
+
+ // dd: These structs are anonymous in the upstream S2 code; however,
+ // this generates CMD-check failure due to the [-Wnested-anon-types]
+ // (anonymous types declared in an anonymous union are an extension)
+ // The approach here just names the types.
+ struct CellIDStruct {
+ EncodedStringVector blocks;
+ uint64 base;
+ uint8 level;
+ bool have_exceptions;
+
+ // TODO(ericv): Use std::atomic_flag to cache the last point decoded in
+ // a thread-safe way. This reduces benchmark times for actual polygon
+ // operations (e.g. S2ClosestEdgeQuery) by about 15%.
+ };
+
+ struct UncompressedStruct {
+ const S2Point* points;
+ };
+
+ enum Format : uint8 {
+ UNCOMPRESSED = 0,
+ CELL_IDS = 1,
+ };
+ Format format_;
+ uint32 size_;
+ union {
+ struct UncompressedStruct uncompressed_;
+ struct CellIDStruct cell_ids_;
+ };
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline size_t EncodedS2PointVector::size() const {
+ return size_;
+}
+
+inline S2Point EncodedS2PointVector::operator[](int i) const {
+ switch (format_) {
+ case Format::UNCOMPRESSED:
+ return uncompressed_.points[i];
+
+ case Format::CELL_IDS:
+ return DecodeCellIdsFormat(i);
+
+ default:
+ S2_LOG(DFATAL) << "Unrecognized format";
+ return S2Point();
+ }
+}
+
+} // namespace s2coding
+
+#endif // S2_ENCODED_S2POINT_VECTOR_H_
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_ENCODED_S2SHAPE_INDEX_H_
+#define S2_ENCODED_S2SHAPE_INDEX_H_
+
+#include "s2/encoded_s2cell_id_vector.h"
+#include "s2/encoded_string_vector.h"
+#include "s2/mutable_s2shape_index.h"
+
+class EncodedS2ShapeIndex final : public S2ShapeIndex {
+ public:
+ using Options = MutableS2ShapeIndex::Options;
+ using ShapeFactory = S2ShapeIndex::ShapeFactory;
+
+ // Creates an index that must be initialized by calling Init().
+ EncodedS2ShapeIndex();
+
+ ~EncodedS2ShapeIndex() override;
+
+ // Initializes the EncodedS2ShapeIndex, returning true on success.
+ //
+ // This method does not decode the S2Shape objects in the index; this is
+ // the responsibility of the client-provided function "shape_factory"
+ // (see s2shapeutil_coding.h). Example usage:
+ //
+ // index.Init(decoder, s2shapeutil::LazyDecodeShapeFactory(decoder));
+ //
+ // Note that the encoded shape vector must *precede* the encoded S2ShapeIndex
+ // in the Decoder's data buffer in this example.
+ bool Init(Decoder* decoder, const ShapeFactory& shape_factory);
+
+ const Options& options() const { return options_; }
+
+ // The number of distinct shape ids in the index. This equals the number of
+ // shapes in the index provided that no shapes have ever been removed.
+ // (Shape ids are not reused.)
+ int num_shape_ids() const override { return shapes_.size(); }
+
+ // Return a pointer to the shape with the given id, or nullptr if the shape
+ // has been removed from the index.
+ S2Shape* shape(int id) const override;
+
+ // Minimizes memory usage by requesting that any data structures that can be
+ // rebuilt should be discarded. This method invalidates all iterators.
+ //
+ // Like all non-const methods, this method is not thread-safe.
+ void Minimize() override;
+
+ class Iterator final : public IteratorBase {
+ public:
+ // Default constructor; must be followed by a call to Init().
+ Iterator();
+
+ // Constructs an iterator positioned as specified. By default iterators
+ // are unpositioned, since this avoids an extra seek in this situation
+ // where one of the seek methods (such as Locate) is immediately called.
+ //
+ // If you want to position the iterator at the beginning, e.g. in order to
+ // loop through the entire index, do this instead:
+ //
+ // for (EncodedS2ShapeIndex::Iterator it(&index, S2ShapeIndex::BEGIN);
+ // !it.done(); it.Next()) { ... }
+ explicit Iterator(const EncodedS2ShapeIndex* index,
+ InitialPosition pos = UNPOSITIONED);
+
+ // Initializes an iterator for the given EncodedS2ShapeIndex.
+ void Init(const EncodedS2ShapeIndex* index,
+ InitialPosition pos = UNPOSITIONED);
+
+ // Inherited non-virtual methods:
+ // S2CellId id() const;
+ // const S2ShapeIndexCell& cell() const;
+ // bool done() const;
+ // S2Point center() const;
+
+ // IteratorBase API:
+ void Begin() override;
+ void Finish() override;
+ void Next() override;
+ bool Prev() override;
+ void Seek(S2CellId target) override;
+ bool Locate(const S2Point& target) override;
+ CellRelation Locate(S2CellId target) override;
+
+ protected:
+ const S2ShapeIndexCell* GetCell() const override;
+ std::unique_ptr<IteratorBase> Clone() const override;
+ void Copy(const IteratorBase& other) override;
+
+ private:
+ void Refresh(); // Updates the IteratorBase fields.
+ const EncodedS2ShapeIndex* index_;
+ int32 cell_pos_; // Current position in the vector of index cells.
+ int32 num_cells_;
+ };
+
+ // Returns the number of bytes currently occupied by the index (including any
+ // unused space at the end of vectors, etc). It has the same thread safety
+ // as the other "const" methods (see introduction).
+ size_t SpaceUsed() const override;
+
+ protected:
+ std::unique_ptr<IteratorBase> NewIterator(InitialPosition pos) const override;
+
+ private:
+ friend class Iterator;
+
+ // Returns a value indicating that a shape has not been decoded yet.
+ inline static S2Shape* kUndecodedShape() {
+ return reinterpret_cast<S2Shape*>(1);
+ }
+
+ // Like std::atomic<S2Shape*>, but defaults to kUndecodedShape().
+ class AtomicShape : public std::atomic<S2Shape*> {
+ public:
+ AtomicShape() : std::atomic<S2Shape*>(kUndecodedShape()) {}
+ };
+
+ S2Shape* GetShape(int id) const;
+ const S2ShapeIndexCell* GetCell(int i) const;
+ bool cell_decoded(int i) const;
+ bool test_and_set_cell_decoded(int i) const;
+ int max_cell_cache_size() const;
+
+ std::unique_ptr<ShapeFactory> shape_factory_;
+
+ // The options specified for this index.
+ Options options_;
+
+ // A vector containing all shapes in the index. Initially all shapes are
+ // set to kUndecodedShape(); as shapes are decoded, they are added to the
+ // vector using std::atomic::compare_exchange_strong.
+ mutable std::vector<AtomicShape> shapes_;
+
+ // A vector containing the S2CellIds of each cell in the index.
+ s2coding::EncodedS2CellIdVector cell_ids_;
+
+ // A vector containing the encoded contents of each cell in the index.
+ s2coding::EncodedStringVector encoded_cells_;
+
+ // A raw array containing the decoded contents of each cell in the index.
+ // Initially all values are *uninitialized memory*. The cells_decoded_
+ // field below keeps track of which elements are present.
+ mutable std::unique_ptr<std::atomic<S2ShapeIndexCell*>[]> cells_;
+
+ // A bit vector indicating which elements of cells_ have been decoded.
+ // All other elements of cells_ contain uninitialized (random) memory.
+ mutable std::vector<std::atomic<uint64>> cells_decoded_;
+
+ // In order to minimize destructor time when very few cells of a large
+ // S2ShapeIndex are needed, we keep track of the indices of the first few
+ // cells to be decoded. This lets us avoid scanning the cells_decoded_
+ // vector when the number of cells decoded is very small.
+ mutable std::vector<int> cell_cache_;
+
+ // Protects all updates to cells_ and cells_decoded_.
+ mutable SpinLock cells_lock_;
+
+ EncodedS2ShapeIndex(const EncodedS2ShapeIndex&) = delete;
+ void operator=(const EncodedS2ShapeIndex&) = delete;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline EncodedS2ShapeIndex::Iterator::Iterator() : index_(nullptr) {
+}
+
+inline EncodedS2ShapeIndex::Iterator::Iterator(
+ const EncodedS2ShapeIndex* index, InitialPosition pos) {
+ Init(index, pos);
+}
+
+inline void EncodedS2ShapeIndex::Iterator::Init(
+ const EncodedS2ShapeIndex* index, InitialPosition pos) {
+ index_ = index;
+ num_cells_ = index->cell_ids_.size();
+ cell_pos_ = (pos == BEGIN) ? 0 : num_cells_;
+ Refresh();
+}
+
+inline void EncodedS2ShapeIndex::Iterator::Refresh() {
+ if (cell_pos_ == num_cells_) {
+ set_finished();
+ } else {
+ // It's faster to initialize the cell to nullptr even if it has already
+ // been decoded, since algorithms frequently don't need it (i.e., based on
+ // the S2CellId they might not need to look at the cell contents).
+ set_state(index_->cell_ids_[cell_pos_], nullptr);
+ }
+}
+
+inline void EncodedS2ShapeIndex::Iterator::Begin() {
+ cell_pos_ = 0;
+ Refresh();
+}
+
+inline void EncodedS2ShapeIndex::Iterator::Finish() {
+ cell_pos_ = num_cells_;
+ Refresh();
+}
+
+inline void EncodedS2ShapeIndex::Iterator::Next() {
+ S2_DCHECK(!done());
+ ++cell_pos_;
+ Refresh();
+}
+
+inline bool EncodedS2ShapeIndex::Iterator::Prev() {
+ if (cell_pos_ == 0) return false;
+ --cell_pos_;
+ Refresh();
+ return true;
+}
+
+inline void EncodedS2ShapeIndex::Iterator::Seek(S2CellId target) {
+ cell_pos_ = index_->cell_ids_.lower_bound(target);
+ Refresh();
+}
+
+inline std::unique_ptr<EncodedS2ShapeIndex::IteratorBase>
+EncodedS2ShapeIndex::NewIterator(InitialPosition pos) const {
+ return absl::make_unique<Iterator>(this, pos);
+}
+
+inline S2Shape* EncodedS2ShapeIndex::shape(int id) const {
+ S2Shape* shape = shapes_[id].load(std::memory_order_relaxed);
+ if (shape != kUndecodedShape()) return shape;
+ return GetShape(id);
+}
+
+// Returns true if the given cell has been decoded yet.
+inline bool EncodedS2ShapeIndex::cell_decoded(int i) const {
+ uint64 group_bits = cells_decoded_[i >> 6].load(std::memory_order_relaxed);
+ return (group_bits & (1ULL << (i & 63))) != 0;
+}
+
+// Marks the given cell as decoded and returns true if it was already marked.
+inline bool EncodedS2ShapeIndex::test_and_set_cell_decoded(int i) const {
+ std::atomic<uint64>* group = &cells_decoded_[i >> 6];
+ uint64 group_bits = group->load(std::memory_order_relaxed);
+ uint64 test_bit = 1ULL << (i & 63);
+ group->store(group_bits | test_bit, std::memory_order_relaxed);
+ return (group_bits & test_bit) != 0;
+}
+
+inline int EncodedS2ShapeIndex::max_cell_cache_size() const {
+ // The cell cache is sized so that scanning decoded_cells_ in the destructor
+ // costs about 30 cycles per decoded cell in the worst case. (This overhead
+ // is acceptable relative to the other costs of decoding each cell.)
+ //
+ // For example, if there are 65,536 cells then we won't need to scan
+ // encoded_cells_ unless we decode at least (65536/2048) == 32 cells. It
+ // takes about 1 cycle per 64 cells to scan encoded_cells_, so that works
+ // out to (65536/64) == 1024 cycles. However this cost is amortized over
+ // the 32 cells decoded, which works out to 32 cycles per cell.
+ return cell_ids_.size() >> 11;
+}
+
+#endif // S2_ENCODED_S2SHAPE_INDEX_H_
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_ENCODED_STRING_VECTOR_H_
+#define S2_ENCODED_STRING_VECTOR_H_
+
+#include <memory>
+#include <string>
+#include "s2/third_party/absl/strings/string_view.h"
+#include "s2/third_party/absl/types/span.h"
+#include "s2/encoded_uint_vector.h"
+
+namespace s2coding {
+
+// This class allows an EncodedStringVector to be created by adding strings
+// incrementally. It also supports adding strings that are the output of
+// another Encoder. For example, to create a vector of encoded S2Polygons,
+// you can do this:
+//
+// void EncodePolygons(const vector<S2Polygon*>& polygons, Encoder* encoder) {
+// StringVectorEncoder encoded_polygons;
+// for (auto polygon : polygons) {
+// polygon->Encode(encoded_polygons.AddViaEncoder());
+// }
+// encoded_polygons.Encode(encoder);
+// }
+class StringVectorEncoder {
+ public:
+ StringVectorEncoder();
+
+ // Adds a string to the encoded vector.
+ void Add(const string& str);
+
+ // Adds a string to the encoded vector by means of the given Encoder. The
+ // string consists of all output added to the encoder before the next call
+ // to any method of this class (after which the encoder is no longer valid).
+ Encoder* AddViaEncoder();
+
+ // Appends the EncodedStringVector representation to the given Encoder.
+ //
+ // REQUIRES: "encoder" uses the default constructor, so that its buffer
+ // can be enlarged as necessary by calling Ensure(int).
+ void Encode(Encoder* encoder);
+
+ // Encodes a vector of strings in a format that can later be decoded as an
+ // EncodedStringVector.
+ //
+ // REQUIRES: "encoder" uses the default constructor, so that its buffer
+ // can be enlarged as necessary by calling Ensure(int).
+ static void Encode(absl::Span<const string> v, Encoder* encoder);
+
+ private:
+ // A vector consisting of the starting offset of each string in the
+ // encoder's data buffer, plus a final entry pointing just past the end of
+ // the last string.
+ std::vector<uint64> offsets_;
+ Encoder data_;
+};
+
+// This class represents an encoded vector of strings. Values are decoded
+// only when they are accessed. This allows for very fast initialization and
+// no additional memory use beyond the encoded data. The encoded data is not
+// owned by this class; typically it points into a large contiguous buffer
+// that contains other encoded data as well.
+//
+// This is one of several helper classes that allow complex data structures to
+// be initialized from an encoded format in constant time and then decoded on
+// demand. This can be a big performance advantage when only a small part of
+// the data structure is actually used.
+class EncodedStringVector {
+ public:
+ // Constructs an uninitialized object; requires Init() to be called.
+ EncodedStringVector() {}
+
+ // Initializes the EncodedStringVector. Returns false on errors, leaving
+ // the vector in an unspecified state.
+ //
+ // REQUIRES: The Decoder data buffer must outlive this object.
+ bool Init(Decoder* decoder);
+
+ // Resets the vector to be empty.
+ void Clear();
+
+ // Returns the size of the original vector.
+ size_t size() const;
+
+ // Returns the string at the given index.
+ absl::string_view operator[](int i) const;
+
+ // Returns a Decoder initialized with the string at the given index.
+ Decoder GetDecoder(int i) const;
+
+ // Returns a pointer to the start of the string at the given index. This is
+ // faster than operator[] but returns an unbounded string.
+ const char* GetStart(int i) const;
+
+ // Returns the entire vector of original strings. Requires that the
+ // data buffer passed to the constructor persists until the result vector is
+ // no longer needed.
+ std::vector<absl::string_view> Decode() const;
+
+ private:
+ EncodedUintVector<uint64> offsets_;
+ const char* data_;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline void StringVectorEncoder::Add(const string& str) {
+ offsets_.push_back(data_.length());
+ data_.Ensure(str.size());
+ data_.putn(str.data(), str.size());
+}
+
+inline Encoder* StringVectorEncoder::AddViaEncoder() {
+ offsets_.push_back(data_.length());
+ return &data_;
+}
+
+inline void EncodedStringVector::Clear() {
+ offsets_.Clear();
+ data_ = nullptr;
+}
+
+inline size_t EncodedStringVector::size() const {
+ return offsets_.size();
+}
+
+inline absl::string_view EncodedStringVector::operator[](int i) const {
+ uint64 start = (i == 0) ? 0 : offsets_[i - 1];
+ uint64 limit = offsets_[i];
+ return absl::string_view(data_ + start, limit - start);
+}
+
+inline Decoder EncodedStringVector::GetDecoder(int i) const {
+ uint64 start = (i == 0) ? 0 : offsets_[i - 1];
+ uint64 limit = offsets_[i];
+ return Decoder(data_ + start, limit - start);
+}
+
+inline const char* EncodedStringVector::GetStart(int i) const {
+ uint64 start = (i == 0) ? 0 : offsets_[i - 1];
+ return data_ + start;
+}
+
+} // namespace s2coding
+
+#endif // S2_ENCODED_STRING_VECTOR_H_
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_ENCODED_UINT_VECTOR_H_
+#define S2_ENCODED_UINT_VECTOR_H_
+
+#include <type_traits>
+#include <vector>
+#include "s2/third_party/absl/base/internal/unaligned_access.h"
+#include "s2/third_party/absl/types/span.h"
+#include "s2/util/coding/coder.h"
+
+namespace s2coding {
+
+// Encodes a vector of unsigned integers in a format that can later be
+// decoded as an EncodedUintVector.
+//
+// REQUIRES: T is an unsigned integer type.
+// REQUIRES: 2 <= sizeof(T) <= 8
+// REQUIRES: "encoder" uses the default constructor, so that its buffer
+// can be enlarged as necessary by calling Ensure(int).
+template <class T>
+void EncodeUintVector(absl::Span<const T> v, Encoder* encoder);
+
+// This class represents an encoded vector of unsigned integers of type T.
+// Values are decoded only when they are accessed. This allows for very fast
+// initialization and no additional memory use beyond the encoded data.
+// The encoded data is not owned by this class; typically it points into a
+// large contiguous buffer that contains other encoded data as well.
+//
+// This is one of several helper classes that allow complex data structures to
+// be initialized from an encoded format in constant time and then decoded on
+// demand. This can be a big performance advantage when only a small part of
+// the data structure is actually used.
+//
+// Values are encoded using a fixed number of bytes per value, where the
+// number of bytes depends on the largest value present.
+//
+// REQUIRES: T is an unsigned integer type.
+// REQUIRES: 2 <= sizeof(T) <= 8
+template <class T>
+class EncodedUintVector {
+ public:
+ static_assert(std::is_unsigned<T>::value, "Unsupported signed integer");
+ static_assert(sizeof(T) & 0xe, "Unsupported integer length");
+
+ // Constructs an uninitialized object; requires Init() to be called.
+ EncodedUintVector() {}
+
+ // Initializes the EncodedUintVector. Returns false on errors, leaving the
+ // vector in an unspecified state.
+ //
+ // REQUIRES: The Decoder data buffer must outlive this object.
+ bool Init(Decoder* decoder);
+
+ // Resets the vector to be empty.
+ void Clear();
+
+ // Returns the size of the original vector.
+ size_t size() const;
+
+ // Returns the element at the given index.
+ T operator[](int i) const;
+
+ // Returns the index of the first element x such that (x >= target), or
+ // size() if no such element exists.
+ //
+ // REQUIRES: The vector elements are sorted in non-decreasing order.
+ size_t lower_bound(T target) const;
+
+ // Decodes and returns the entire original vector.
+ std::vector<T> Decode() const;
+
+ private:
+ template <int length> size_t lower_bound(T target) const;
+
+ const char* data_;
+ uint32 size_;
+ uint8 len_;
+};
+
+// Encodes an unsigned integer in little-endian format using "length" bytes.
+// (The client must ensure that the encoder's buffer is large enough.)
+//
+// REQUIRES: T is an unsigned integer type.
+// REQUIRES: 2 <= sizeof(T) <= 8
+// REQUIRES: 0 <= length <= sizeof(T)
+// REQUIRES: value < 256 ** length
+// REQUIRES: encoder->avail() >= length
+template <class T>
+void EncodeUintWithLength(T value, int length, Encoder* encoder);
+
+// Decodes a variable-length integer consisting of "length" bytes starting at
+// "ptr" in little-endian format.
+//
+// REQUIRES: T is an unsigned integer type.
+// REQUIRES: 2 <= sizeof(T) <= 8
+// REQUIRES: 0 <= length <= sizeof(T)
+template <class T>
+T GetUintWithLength(const void* ptr, int length);
+
+// Decodes and consumes a variable-length integer consisting of "length" bytes
+// in little-endian format. Returns false if not enough bytes are available.
+//
+// REQUIRES: T is an unsigned integer type.
+// REQUIRES: 2 <= sizeof(T) <= 8
+// REQUIRES: 0 <= length <= sizeof(T)
+template <class T>
+bool DecodeUintWithLength(int length, Decoder* decoder, T* result);
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+template <class T>
+inline void EncodeUintWithLength(T value, int length, Encoder* encoder) {
+ static_assert(std::is_unsigned<T>::value, "Unsupported signed integer");
+ static_assert(sizeof(T) & 0xe, "Unsupported integer length");
+ S2_DCHECK(length >= 0 && length <= sizeof(T));
+ S2_DCHECK_GE(encoder->avail(), length);
+
+ while (--length >= 0) {
+ encoder->put8(value);
+ value >>= 8;
+ }
+ S2_DCHECK_EQ(value, 0);
+}
+
+template <class T>
+inline T GetUintWithLength(const char* ptr, int length) {
+ static_assert(std::is_unsigned<T>::value, "Unsupported signed integer");
+ static_assert(sizeof(T) & 0xe, "Unsupported integer length");
+ S2_DCHECK(length >= 0 && length <= sizeof(T));
+
+ // Note that the following code is faster than any of the following:
+ //
+ // - A loop that repeatedly loads and shifts one byte.
+ // - memcpying "length" bytes to a local variable of type T.
+ // - A switch statement that handles each length optimally.
+ //
+ // The following code is slightly faster:
+ //
+ // T mask = (length == 0) ? 0 : ~T{0} >> 8 * (sizeof(T) - length);
+ // return *reinterpret_cast<const T*>(ptr) & mask;
+ //
+ // However this technique is unsafe because in extremely rare cases it might
+ // access out-of-bounds heap memory. (This can only happen if "ptr" is
+ // within (sizeof(T) - length) bytes of the end of a memory page and the
+ // following page in the address space is unmapped.)
+
+ if (length & sizeof(T)) {
+ if (sizeof(T) == 8) return ABSL_INTERNAL_UNALIGNED_LOAD64(ptr);
+ if (sizeof(T) == 4) return ABSL_INTERNAL_UNALIGNED_LOAD32(ptr);
+ if (sizeof(T) == 2) return ABSL_INTERNAL_UNALIGNED_LOAD16(ptr);
+ S2_DCHECK_EQ(sizeof(T), 1);
+ return *ptr;
+ }
+ T x = 0;
+ ptr += length;
+ if (sizeof(T) > 4 && (length & 4)) {
+ x = ABSL_INTERNAL_UNALIGNED_LOAD32(ptr -= sizeof(uint32));
+ }
+ if (sizeof(T) > 2 && (length & 2)) {
+ x = (x << 16) + ABSL_INTERNAL_UNALIGNED_LOAD16(ptr -= sizeof(uint16));
+ }
+ if (sizeof(T) > 1 && (length & 1)) {
+ x = (x << 8) + static_cast<uint8>(*--ptr);
+ }
+ return x;
+}
+
+template <class T>
+bool DecodeUintWithLength(int length, Decoder* decoder, T* result) {
+ if (decoder->avail() < length) return false;
+ const char* ptr = reinterpret_cast<const char*>(decoder->ptr());
+ *result = GetUintWithLength<T>(ptr, length);
+ decoder->skip(length);
+ return true;
+}
+
+template <class T>
+void EncodeUintVector(absl::Span<const T> v, Encoder* encoder) {
+ // The encoding is as follows:
+ //
+ // varint64: (v.size() * sizeof(T)) | (len - 1)
+ // array of v.size() elements ["len" bytes each]
+ //
+ // Note that we don't allow (len == 0) since this would require an extra bit
+ // to encode the length.
+
+ T one_bits = 1; // Ensures len >= 1.
+ for (auto x : v) one_bits |= x;
+ int len = (Bits::FindMSBSetNonZero64(one_bits) >> 3) + 1;
+ S2_DCHECK(len >= 1 && len <= 8);
+
+ // Note that the multiplication is optimized into a bit shift.
+ encoder->Ensure(Varint::kMax64 + v.size() * len);
+ uint64 size_len = (uint64{v.size()} * sizeof(T)) | (len - 1);
+ encoder->put_varint64(size_len);
+ for (auto x : v) {
+ EncodeUintWithLength(x, len, encoder);
+ }
+}
+
+template <class T>
+bool EncodedUintVector<T>::Init(Decoder* decoder) {
+ uint64 size_len;
+ if (!decoder->get_varint64(&size_len)) return false;
+ size_ = size_len / sizeof(T); // Optimized into bit shift.
+ len_ = (size_len & (sizeof(T) - 1)) + 1;
+ if (size_ > std::numeric_limits<size_t>::max() / sizeof(T)) return false;
+ size_t bytes = size_ * len_;
+ if (decoder->avail() < bytes) return false;
+ data_ = reinterpret_cast<const char*>(decoder->ptr());
+ decoder->skip(bytes);
+ return true;
+}
+
+template <class T>
+void EncodedUintVector<T>::Clear() {
+ size_ = 0;
+ data_ = nullptr;
+}
+
+template <class T>
+inline size_t EncodedUintVector<T>::size() const {
+ return size_;
+}
+
+template <class T>
+inline T EncodedUintVector<T>::operator[](int i) const {
+ S2_DCHECK(i >= 0 && i < size_);
+ return GetUintWithLength<T>(data_ + i * len_, len_);
+}
+
+template <class T>
+size_t EncodedUintVector<T>::lower_bound(T target) const {
+ static_assert(sizeof(T) & 0xe, "Unsupported integer length");
+ S2_DCHECK(len_ >= 1 && len_ <= sizeof(T));
+
+ // TODO(ericv): Consider using the unused 28 bits of "len_" to store the
+ // last result of lower_bound() to be used as a hint. This should help in
+ // common situation where the same element is looked up repeatedly. This
+ // would require declaring the new field (length_lower_bound_hint_) as
+ // mutable std::atomic<uint32> (accessed using std::memory_order_relaxed)
+ // with a custom copy constructor that resets the hint component to zero.
+ switch (len_) {
+ case 1: return lower_bound<1>(target);
+ case 2: return lower_bound<2>(target);
+ case 3: return lower_bound<3>(target);
+ case 4: return lower_bound<4>(target);
+ case 5: return lower_bound<5>(target);
+ case 6: return lower_bound<6>(target);
+ case 7: return lower_bound<7>(target);
+ default: return lower_bound<8>(target);
+ }
+}
+
+template <class T> template <int length>
+inline size_t EncodedUintVector<T>::lower_bound(T target) const {
+ size_t lo = 0, hi = size_;
+ while (lo < hi) {
+ size_t mid = (lo + hi) >> 1;
+ T value = GetUintWithLength<T>(data_ + mid * length, length);
+ if (value < target) {
+ lo = mid + 1;
+ } else {
+ hi = mid;
+ }
+ }
+ return lo;
+}
+
+template <class T>
+std::vector<T> EncodedUintVector<T>::Decode() const {
+ std::vector<T> result(size_);
+ for (int i = 0; i < size_; ++i) {
+ result[i] = (*this)[i];
+ }
+ return result;
+}
+
+} // namespace s2coding
+
+#endif // S2_ENCODED_UINT_VECTOR_H_
--- /dev/null
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_ID_SET_LEXICON_H_
+#define S2_ID_SET_LEXICON_H_
+
+#include <limits>
+#include <vector>
+
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+#include "s2/sequence_lexicon.h"
+
+// IdSetLexicon is a class for compactly representing sets of non-negative
+// integers such as array indices ("id sets"). It is especially suitable when
+// either (1) there are many duplicate sets, or (2) there are many singleton
+// or empty sets. See also ValueLexicon and SequenceLexicon.
+//
+// Each distinct id set is mapped to a 32-bit integer. Empty and singleton
+// sets take up no additional space whatsoever; the set itself is represented
+// by the unique id assigned to the set. Sets of size 2 or more occupy about
+// 11 bytes per set plus 4 bytes per element (as compared to 24 bytes per set
+// plus 4 bytes per element for std::vector). Duplicate sets are
+// automatically eliminated. Note also that id sets are referred to using
+// 32-bit integers rather than 64-bit pointers.
+//
+// This class is especially useful in conjunction with ValueLexicon<T>. For
+// example, suppose that you want to label objects with a set of strings. You
+// could use a ValueLexicon<string> to map the strings to "label ids" (32-bit
+// integers), and then use IdSetLexicon to map each set of labels to a "label
+// set id". Each reference to that label set then takes up only 4 bytes.
+//
+// Example usage:
+//
+// ValueLexicon<string> labels_;
+// IdSetLexicon label_sets_;
+//
+// int32 GetLabelSet(const vector<string>& label_strings) {
+// vector<int32> label_ids;
+// for (const auto& str : label_strings) {
+// label_ids.push_back(labels_.Add(str));
+// }
+// return label_sets_.Add(label_ids);
+// }
+//
+// int label_set_id = GetLabelSet(...);
+// for (auto id : label_sets_.id_set(label_set_id)) {
+// S2_LOG(INFO) << id;
+// }
+//
+// This class is similar to SequenceLexicon, except:
+//
+// 1. Empty and singleton sets are represented implicitly; they use no space.
+// 2. Sets are represented rather than sequences; the ordering of values is
+// not important and duplicates are removed.
+// 3. The values must be 32-bit non-negative integers (only).
+class IdSetLexicon {
+ public:
+ IdSetLexicon();
+ ~IdSetLexicon();
+
+ // IdSetLexicon is movable and copyable.
+ IdSetLexicon(const IdSetLexicon&);
+ IdSetLexicon& operator=(const IdSetLexicon&);
+ IdSetLexicon(IdSetLexicon&&);
+ IdSetLexicon& operator=(IdSetLexicon&&);
+
+ // Clears all data from the lexicon.
+ void Clear();
+
+ // Add the given set of integers to the lexicon if it is not already
+ // present, and return the unique id for this set. "begin" and "end" are
+ // forward iterators over a sequence of values that can be converted to
+ // non-negative 32-bit integers. The values are automatically sorted and
+ // duplicates are removed. Returns a signed integer representing this set.
+ //
+ // REQUIRES: All values in [begin, end) are non-negative 32-bit integers.
+ template <class FwdIterator>
+ int32 Add(FwdIterator begin, FwdIterator end);
+
+ // Add the given set of integers to the lexicon if it is not already
+ // present, and return the unique id for this set. This is a convenience
+ // method equivalent to Add(std::begin(container), std::end(container)).
+ template <class Container>
+ int32 Add(const Container& container);
+
+ // Convenience method that returns the unique id for a singleton set.
+ // Note that because singleton sets take up no space, this method is
+ // const. Equivalent to calling Add(&id, &id + 1).
+ int32 AddSingleton(int32 id) const;
+
+ // Convenience method that returns the unique id for the empty set. Note
+ // that because the empty set takes up no space and has a fixed id, this
+ // method is static. Equivalent to calling Add() with an empty container.
+ static int32 EmptySetId();
+
+ // Iterator type; please treat this as an opaque forward iterator.
+ using Iterator = const int32*;
+
+ // This class represents a set of integers stored in the IdSetLexicon.
+ class IdSet {
+ public:
+ Iterator begin() const;
+ Iterator end() const;
+ size_t size() const;
+
+ private:
+ friend class IdSetLexicon;
+ IdSet();
+ IdSet(Iterator begin, Iterator end);
+ explicit IdSet(int32 singleton_id);
+ Iterator begin_, end_;
+ int32 singleton_id_;
+ };
+ // Return the set of integers corresponding to an id returned by Add().
+ IdSet id_set(int32 set_id) const;
+
+ private:
+ // Choose kEmptySetId to be the last id that will ever be generated.
+ // (Non-negative ids are reserved for singleton sets.)
+ static const int32 kEmptySetId = std::numeric_limits<int32>::min();
+ int32 AddInternal(std::vector<int32>* ids);
+
+ SequenceLexicon<int32> id_sets_;
+
+ std::vector<int32> tmp_; // temporary storage used during Add()
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline IdSetLexicon::Iterator IdSetLexicon::IdSet::begin() const {
+ return begin_;
+}
+
+inline IdSetLexicon::Iterator IdSetLexicon::IdSet::end() const {
+ return end_;
+}
+
+inline size_t IdSetLexicon::IdSet::size() const {
+ return end_ - begin_;
+}
+
+inline IdSetLexicon::IdSet::IdSet()
+ : begin_(&singleton_id_), end_(begin_) {
+}
+
+inline IdSetLexicon::IdSet::IdSet(Iterator begin, Iterator end)
+ : begin_(begin), end_(end) {
+}
+
+inline IdSetLexicon::IdSet::IdSet(int32 singleton_id)
+ : begin_(&singleton_id_), end_(&singleton_id_ + 1),
+ singleton_id_(singleton_id) {
+}
+
+inline int32 IdSetLexicon::AddSingleton(int32 id) const {
+ S2_DCHECK_GE(id, 0);
+ S2_DCHECK_LE(id, std::numeric_limits<int32>::max());
+ // Singleton sets are represented by their element.
+ return id;
+}
+
+/*static*/ inline int32 IdSetLexicon::EmptySetId() {
+ return kEmptySetId;
+}
+
+template <class FwdIterator>
+int32 IdSetLexicon::Add(FwdIterator begin, FwdIterator end) {
+ tmp_.clear();
+ for (; begin != end; ++begin) {
+ S2_DCHECK_GE(*begin, 0);
+ S2_DCHECK_LE(*begin, std::numeric_limits<int32>::max());
+ tmp_.push_back(*begin);
+ }
+ return AddInternal(&tmp_);
+}
+
+template <class Container>
+int32 IdSetLexicon::Add(const Container& container) {
+ return Add(std::begin(container), std::end(container));
+}
+
+#endif // S2_ID_SET_LEXICON_H_
--- /dev/null
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_MUTABLE_S2SHAPE_INDEX_H_
+#define S2_MUTABLE_S2SHAPE_INDEX_H_
+
+#include <array>
+#include <atomic>
+#include <cstddef>
+#include <memory>
+#include <utility>
+#include <vector>
+
+
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+#include "s2/base/mutex.h"
+#include "s2/base/spinlock.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2pointutil.h"
+#include "s2/s2shape.h"
+#include "s2/s2shape_index.h"
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/third_party/absl/base/thread_annotations.h"
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/util/gtl/btree_map.h"
+
+// MutableS2ShapeIndex is a class for in-memory indexing of polygonal geometry.
+// The objects in the index are known as "shapes", and may consist of points,
+// polylines, and/or polygons, possibly overlapping. The index makes it very
+// fast to answer queries such as finding nearby shapes, measuring distances,
+// testing for intersection and containment, etc.
+//
+// MutableS2ShapeIndex allows not only building an index, but also updating it
+// incrementally by adding or removing shapes (hence its name). It is one of
+// several implementations of the S2ShapeIndex interface. MutableS2ShapeIndex
+// is designed to be compact; usually it is smaller than the underlying
+// geometry being indexed. It is capable of indexing up to hundreds of
+// millions of edges. The index is also fast to construct.
+//
+// There are a number of built-in classes that work with S2ShapeIndex objects.
+// Generally these classes accept any collection of geometry that can be
+// represented by an S2ShapeIndex, i.e. any combination of points, polylines,
+// and polygons. Such classes include:
+//
+// - S2ContainsPointQuery: returns the shape(s) that contain a given point.
+//
+// - S2ClosestEdgeQuery: returns the closest edge(s) to a given point, edge,
+// S2CellId, or S2ShapeIndex.
+//
+// - S2CrossingEdgeQuery: returns the edge(s) that cross a given edge.
+//
+// - S2BooleanOperation: computes boolean operations such as union,
+// and boolean predicates such as containment.
+//
+// - S2ShapeIndexRegion: computes approximations for a collection of geometry.
+//
+// - S2ShapeIndexBufferedRegion: computes approximations that have been
+// expanded by a given radius.
+//
+// Here is an example showing how to build an index for a set of polygons, and
+// then then determine which polygon(s) contain each of a set of query points:
+//
+// void TestContainment(const vector<S2Point>& points,
+// const vector<S2Polygon*>& polygons) {
+// MutableS2ShapeIndex index;
+// for (auto polygon : polygons) {
+// index.Add(absl::make_unique<S2Polygon::Shape>(polygon));
+// }
+// auto query = MakeS2ContainsPointQuery(&index);
+// for (const auto& point : points) {
+// for (S2Shape* shape : query.GetContainingShapes(point)) {
+// S2Polygon* polygon = polygons[shape->id()];
+// ... do something with (point, polygon) ...
+// }
+// }
+// }
+//
+// This example uses S2Polygon::Shape, which is one example of an S2Shape
+// object. S2Polyline and S2Loop also have nested Shape classes, and there are
+// additional S2Shape types defined in *_shape.h.
+//
+// Internally, MutableS2ShapeIndex is essentially a map from S2CellIds to the
+// set of shapes that intersect each S2CellId. It is adaptively refined to
+// ensure that no cell contains more than a small number of edges.
+//
+// For efficiency, updates are batched together and applied lazily on the
+// first subsequent query. Locking is used to ensure that MutableS2ShapeIndex
+// has the same thread-safety properties as "vector": const methods are
+// thread-safe, while non-const methods are not thread-safe. This means that
+// if one thread updates the index, you must ensure that no other thread is
+// reading or updating the index at the same time.
+//
+// TODO(ericv): MutableS2ShapeIndex has an Encode() method that allows the
+// index to be serialized. An encoded S2ShapeIndex can be decoded either into
+// its original form (MutableS2ShapeIndex) or into an EncodedS2ShapeIndex.
+// The key property of EncodedS2ShapeIndex is that it can be constructed
+// instantaneously, since the index is kept in its original encoded form.
+// Data is decoded only when an operation needs it. For example, to determine
+// which shapes(s) contain a given query point only requires decoding the data
+// in the S2ShapeIndexCell that contains that point.
+class MutableS2ShapeIndex final : public S2ShapeIndex {
+ private:
+ using CellMap = gtl::btree_map<S2CellId, S2ShapeIndexCell*>;
+
+ public:
+ // Options that affect construction of the MutableS2ShapeIndex.
+ class Options {
+ public:
+ Options();
+
+ // The maximum number of edges per cell. If a cell has more than this
+ // many edges that are not considered "long" relative to the cell size,
+ // then it is subdivided. (Whether an edge is considered "long" is
+ // controlled by --s2shape_index_cell_size_to_long_edge_ratio flag.)
+ //
+ // Values between 10 and 50 represent a reasonable balance between memory
+ // usage, construction time, and query time. Small values make queries
+ // faster, while large values make construction faster and use less memory.
+ // Values higher than 50 do not save significant additional memory, and
+ // query times can increase substantially, especially for algorithms that
+ // visit all pairs of potentially intersecting edges (such as polygon
+ // validation), since this is quadratic in the number of edges per cell.
+ //
+ // Note that the *average* number of edges per cell is generally slightly
+ // less than half of the maximum value defined here.
+ //
+ // Defaults to value given by --s2shape_index_default_max_edges_per_cell.
+ int max_edges_per_cell() const { return max_edges_per_cell_; }
+ void set_max_edges_per_cell(int max_edges_per_cell);
+
+ private:
+ int max_edges_per_cell_;
+ };
+
+ // Creates a MutableS2ShapeIndex that uses the default option settings.
+ // Option values may be changed by calling Init().
+ MutableS2ShapeIndex();
+
+ // Create a MutableS2ShapeIndex with the given options.
+ explicit MutableS2ShapeIndex(const Options& options);
+
+ ~MutableS2ShapeIndex() override;
+
+ // Initialize a MutableS2ShapeIndex with the given options. This method may
+ // only be called when the index is empty (i.e. newly created or Reset() has
+ // just been called).
+ void Init(const Options& options);
+
+ const Options& options() const { return options_; }
+
+ // The number of distinct shape ids that have been assigned. This equals
+ // the number of shapes in the index provided that no shapes have ever been
+ // removed. (Shape ids are not reused.)
+ int num_shape_ids() const override {
+ return static_cast<int>(shapes_.size());
+ }
+
+ // Returns a pointer to the shape with the given id, or nullptr if the shape
+ // has been removed from the index.
+ S2Shape* shape(int id) const override { return shapes_[id].get(); }
+
+ // Minimizes memory usage by requesting that any data structures that can be
+ // rebuilt should be discarded. This method invalidates all iterators.
+ //
+ // Like all non-const methods, this method is not thread-safe.
+ void Minimize() override;
+
+ // Appends an encoded representation of the S2ShapeIndex to "encoder".
+ //
+ // This method does not encode the S2Shapes in the index; it is the client's
+ // responsibility to encode them separately. For example:
+ //
+ // s2shapeutil::CompactEncodeTaggedShapes(index, encoder);
+ // index.Encode(encoder);
+ //
+ // REQUIRES: "encoder" uses the default constructor, so that its buffer
+ // can be enlarged as necessary by calling Ensure(int).
+ void Encode(Encoder* encoder) const;
+
+ // Decodes an S2ShapeIndex, returning true on success.
+ //
+ // This method does not decode the S2Shape objects in the index; this is
+ // the responsibility of the client-provided function "shape_factory"
+ // (see s2shapeutil_coding.h). Example usage:
+ //
+ // index.Init(decoder, s2shapeutil::LazyDecodeShapeFactory(decoder));
+ //
+ // Note that the S2Shape vector must be encoded *before* the S2ShapeIndex in
+ // this example.
+ bool Init(Decoder* decoder, const ShapeFactory& shape_factory);
+
+ class Iterator final : public IteratorBase {
+ public:
+ // Default constructor; must be followed by a call to Init().
+ Iterator();
+
+ // Constructs an iterator positioned as specified. By default iterators
+ // are unpositioned, since this avoids an extra seek in this situation
+ // where one of the seek methods (such as Locate) is immediately called.
+ //
+ // If you want to position the iterator at the beginning, e.g. in order to
+ // loop through the entire index, do this instead:
+ //
+ // for (MutableS2ShapeIndex::Iterator it(&index, S2ShapeIndex::BEGIN);
+ // !it.done(); it.Next()) { ... }
+ explicit Iterator(const MutableS2ShapeIndex* index,
+ InitialPosition pos = UNPOSITIONED);
+
+ // Initializes an iterator for the given MutableS2ShapeIndex. This method
+ // may also be called in order to restore an iterator to a valid state
+ // after the underlying index has been updated (although it is usually
+ // easier just to declare a new iterator whenever required, since iterator
+ // construction is cheap).
+ void Init(const MutableS2ShapeIndex* index,
+ InitialPosition pos = UNPOSITIONED);
+
+ // Initialize an iterator for the given MutableS2ShapeIndex without
+ // applying any pending updates. This can be used to observe the actual
+ // current state of the index without modifying it in any way.
+ void InitStale(const MutableS2ShapeIndex* index,
+ InitialPosition pos = UNPOSITIONED);
+
+ // Inherited non-virtual methods:
+ // S2CellId id() const;
+ // bool done() const;
+ // S2Point center() const;
+ const S2ShapeIndexCell& cell() const;
+
+ // IteratorBase API:
+ void Begin() override;
+ void Finish() override;
+ void Next() override;
+ bool Prev() override;
+ void Seek(S2CellId target) override;
+ bool Locate(const S2Point& target) override;
+ CellRelation Locate(S2CellId target) override;
+
+ protected:
+ const S2ShapeIndexCell* GetCell() const override;
+ std::unique_ptr<IteratorBase> Clone() const override;
+ void Copy(const IteratorBase& other) override;
+
+ private:
+ void Refresh(); // Updates the IteratorBase fields.
+ const MutableS2ShapeIndex* index_;
+ CellMap::const_iterator iter_, end_;
+ };
+
+ // Takes ownership of the given shape and adds it to the index. Also
+ // assigns a unique id to the shape (shape->id()) and returns that id.
+ // Shape ids are assigned sequentially starting from 0 in the order shapes
+ // are added. Invalidates all iterators and their associated data.
+ int Add(std::unique_ptr<S2Shape> shape);
+
+ // Removes the given shape from the index and return ownership to the caller.
+ // Invalidates all iterators and their associated data.
+ std::unique_ptr<S2Shape> Release(int shape_id);
+
+ // Resets the index to its original state and returns ownership of all
+ // shapes to the caller. This method is much more efficient than removing
+ // all shapes one at a time.
+ std::vector<std::unique_ptr<S2Shape>> ReleaseAll();
+
+ // Resets the index to its original state and deletes all shapes. Any
+ // options specified via Init() are preserved.
+ void Clear();
+
+ // Returns the number of bytes currently occupied by the index (including any
+ // unused space at the end of vectors, etc). It has the same thread safety
+ // as the other "const" methods (see introduction).
+ size_t SpaceUsed() const override;
+
+ // Calls to Add() and Release() are normally queued and processed on the
+ // first subsequent query (in a thread-safe way). This has many advantages,
+ // the most important of which is that sometimes there *is* no subsequent
+ // query, which lets us avoid building the index completely.
+ //
+ // This method forces any pending updates to be applied immediately.
+ // Calling this method is rarely a good idea. (One valid reason is to
+ // exclude the cost of building the index from benchmark results.)
+ void ForceBuild();
+
+ // Returns true if there are no pending updates that need to be applied.
+ // This can be useful to avoid building the index unnecessarily, or for
+ // choosing between two different algorithms depending on whether the index
+ // is available.
+ //
+ // The returned index status may be slightly out of date if the index was
+ // built in a different thread. This is fine for the intended use (as an
+ // efficiency hint), but it should not be used by internal methods (see
+ // MaybeApplyUpdates).
+ bool is_fresh() const;
+
+ protected:
+ std::unique_ptr<IteratorBase> NewIterator(InitialPosition pos) const override;
+
+ private:
+ friend class EncodedS2ShapeIndex;
+ friend class Iterator;
+ friend class MutableS2ShapeIndexTest;
+ friend class S2Stats;
+
+ struct BatchDescriptor;
+ struct ClippedEdge;
+ class EdgeAllocator;
+ struct FaceEdge;
+ class InteriorTracker;
+ struct RemovedShape;
+
+ using ShapeIdSet = std::vector<int>;
+
+ // When adding a new encoding, be aware that old binaries will not be able
+ // to decode it.
+ static const unsigned char kCurrentEncodingVersionNumber = 0;
+
+ // Internal methods are documented with their definitions.
+ bool is_first_update() const;
+ bool is_shape_being_removed(int shape_id) const;
+ void MaybeApplyUpdates() const;
+ void ApplyUpdatesThreadSafe();
+ void ApplyUpdatesInternal();
+ void GetUpdateBatches(std::vector<BatchDescriptor>* batches) const;
+ static void GetBatchSizes(int num_items, int max_batches,
+ double final_bytes_per_item,
+ double high_water_bytes_per_item,
+ double preferred_max_bytes_per_batch,
+ std::vector<int>* batch_sizes);
+ void ReserveSpace(const BatchDescriptor& batch,
+ std::vector<FaceEdge> all_edges[6]) const;
+ void AddShape(int id, std::vector<FaceEdge> all_edges[6],
+ InteriorTracker* tracker) const;
+ void RemoveShape(const RemovedShape& removed,
+ std::vector<FaceEdge> all_edges[6],
+ InteriorTracker* tracker) const;
+ void AddFaceEdge(FaceEdge* edge, std::vector<FaceEdge> all_edges[6]) const;
+ void UpdateFaceEdges(int face, const std::vector<FaceEdge>& face_edges,
+ InteriorTracker* tracker);
+ S2CellId ShrinkToFit(const S2PaddedCell& pcell, const R2Rect& bound) const;
+ void SkipCellRange(S2CellId begin, S2CellId end, InteriorTracker* tracker,
+ EdgeAllocator* alloc, bool disjoint_from_index);
+ void UpdateEdges(const S2PaddedCell& pcell,
+ std::vector<const ClippedEdge*>* edges,
+ InteriorTracker* tracker, EdgeAllocator* alloc,
+ bool disjoint_from_index);
+ void AbsorbIndexCell(const S2PaddedCell& pcell,
+ const Iterator& iter,
+ std::vector<const ClippedEdge*>* edges,
+ InteriorTracker* tracker,
+ EdgeAllocator* alloc);
+ int GetEdgeMaxLevel(const S2Shape::Edge& edge) const;
+ static int CountShapes(const std::vector<const ClippedEdge*>& edges,
+ const ShapeIdSet& cshape_ids);
+ bool MakeIndexCell(const S2PaddedCell& pcell,
+ const std::vector<const ClippedEdge*>& edges,
+ InteriorTracker* tracker);
+ static void TestAllEdges(const std::vector<const ClippedEdge*>& edges,
+ InteriorTracker* tracker);
+ inline static const ClippedEdge* UpdateBound(const ClippedEdge* edge,
+ int u_end, double u,
+ int v_end, double v,
+ EdgeAllocator* alloc);
+ static const ClippedEdge* ClipUBound(const ClippedEdge* edge,
+ int u_end, double u,
+ EdgeAllocator* alloc);
+ static const ClippedEdge* ClipVBound(const ClippedEdge* edge,
+ int v_end, double v,
+ EdgeAllocator* alloc);
+ static void ClipVAxis(const ClippedEdge* edge, const R1Interval& middle,
+ std::vector<const ClippedEdge*> child_edges[2],
+ EdgeAllocator* alloc);
+
+ // The amount by which cells are "padded" to compensate for numerical errors
+ // when clipping line segments to cell boundaries.
+ static const double kCellPadding;
+
+ // The shapes in the index, accessed by their shape id. Removed shapes are
+ // replaced by nullptr pointers.
+ std::vector<std::unique_ptr<S2Shape>> shapes_;
+
+ // A map from S2CellId to the set of clipped shapes that intersect that
+ // cell. The cell ids cover a set of non-overlapping regions on the
+ // sphere. Note that this field is updated lazily (see below). Const
+ // methods *must* call MaybeApplyUpdates() before accessing this field.
+ // (The easiest way to achieve this is simply to use an Iterator.)
+ CellMap cell_map_;
+
+ // The options supplied for this index.
+ Options options_;
+
+ // The id of the first shape that has been queued for addition but not
+ // processed yet.
+ int pending_additions_begin_ = 0;
+
+ // The representation of an edge that has been queued for removal.
+ struct RemovedShape {
+ int32 shape_id;
+ bool has_interior; // Belongs to a shape of dimension 2.
+ bool contains_tracker_origin;
+ std::vector<S2Shape::Edge> edges;
+ };
+
+ // The set of shapes that have been queued for removal but not processed
+ // yet. Note that we need to copy the edge data since the caller is free to
+ // destroy the shape once Release() has been called. This field is present
+ // only when there are removed shapes to process (to save memory).
+ std::unique_ptr<std::vector<RemovedShape>> pending_removals_;
+
+ // Additions and removals are queued and processed on the first subsequent
+ // query. There are several reasons to do this:
+ //
+ // - It is significantly more efficient to process updates in batches.
+ // - Often the index will never be queried, in which case we can save both
+ // the time and memory required to build it. Examples:
+ // + S2Loops that are created simply to pass to an S2Polygon. (We don't
+ // need the S2Loop index, because S2Polygon builds its own index.)
+ // + Applications that load a database of geometry and then query only
+ // a small fraction of it.
+ // + Applications that only read and write geometry (Decode/Encode).
+ //
+ // The main drawback is that we need to go to some extra work to ensure that
+ // "const" methods are still thread-safe. Note that the goal is *not* to
+ // make this class thread-safe in general, but simply to hide the fact that
+ // we defer some of the indexing work until query time.
+ //
+ // The textbook approach to this problem would be to use a mutex and a
+ // condition variable. Unfortunately pthread mutexes are huge (40 bytes).
+ // Instead we use spinlock (which is only 4 bytes) to guard a few small
+ // fields representing the current update status, and only create additional
+ // state while the update is actually occurring.
+ mutable SpinLock lock_;
+
+ enum IndexStatus {
+ STALE, // There are pending updates.
+ UPDATING, // Updates are currently being applied.
+ FRESH, // There are no pending updates.
+ };
+ // Reads and writes to this field are guarded by "lock_".
+ std::atomic<IndexStatus> index_status_;
+
+ // UpdateState holds temporary data related to thread synchronization. It
+ // is only allocated while updates are being applied.
+ struct UpdateState {
+ // This mutex is used as a condition variable. It is locked by the
+ // updating thread for the entire duration of the update; other threads
+ // lock it in order to wait until the update is finished.
+ absl::Mutex wait_mutex;
+
+ // The number of threads currently waiting on "wait_mutex_". The
+ // UpdateState can only be freed when this number reaches zero.
+ //
+ // Reads and writes to this field are guarded by "lock_".
+ int num_waiting;
+
+ UpdateState() : num_waiting(0) {
+ }
+
+ ~UpdateState() {
+ S2_DCHECK_EQ(0, num_waiting);
+ }
+ };
+ std::unique_ptr<UpdateState> update_state_;
+
+ // Documented in the .cc file.
+ void UnlockAndSignal()
+ UNLOCK_FUNCTION(lock_)
+ UNLOCK_FUNCTION(update_state_->wait_mutex);
+
+ MutableS2ShapeIndex(const MutableS2ShapeIndex&) = delete;
+ void operator=(const MutableS2ShapeIndex&) = delete;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline MutableS2ShapeIndex::Iterator::Iterator() : index_(nullptr) {
+}
+
+inline MutableS2ShapeIndex::Iterator::Iterator(
+ const MutableS2ShapeIndex* index, InitialPosition pos) {
+ Init(index, pos);
+}
+
+inline void MutableS2ShapeIndex::Iterator::Init(
+ const MutableS2ShapeIndex* index, InitialPosition pos) {
+ index->MaybeApplyUpdates();
+ InitStale(index, pos);
+}
+
+inline void MutableS2ShapeIndex::Iterator::InitStale(
+ const MutableS2ShapeIndex* index, InitialPosition pos) {
+ index_ = index;
+ end_ = index_->cell_map_.end();
+ if (pos == BEGIN) {
+ iter_ = index_->cell_map_.begin();
+ } else {
+ iter_ = end_;
+ }
+ Refresh();
+}
+
+inline const S2ShapeIndexCell& MutableS2ShapeIndex::Iterator::cell() const {
+ // Since MutableS2ShapeIndex always sets the "cell_" field, we can skip the
+ // logic in the base class that conditionally calls GetCell().
+ return *raw_cell();
+}
+
+inline void MutableS2ShapeIndex::Iterator::Refresh() {
+ if (iter_ == end_) {
+ set_finished();
+ } else {
+ set_state(iter_->first, iter_->second);
+ }
+}
+
+inline void MutableS2ShapeIndex::Iterator::Begin() {
+ // Make sure that the index has not been modified since Init() was called.
+ S2_DCHECK(index_->is_fresh());
+ iter_ = index_->cell_map_.begin();
+ Refresh();
+}
+
+inline void MutableS2ShapeIndex::Iterator::Finish() {
+ iter_ = end_;
+ Refresh();
+}
+
+inline void MutableS2ShapeIndex::Iterator::Next() {
+ S2_DCHECK(!done());
+ ++iter_;
+ Refresh();
+}
+
+inline bool MutableS2ShapeIndex::Iterator::Prev() {
+ if (iter_ == index_->cell_map_.begin()) return false;
+ --iter_;
+ Refresh();
+ return true;
+}
+
+inline void MutableS2ShapeIndex::Iterator::Seek(S2CellId target) {
+ iter_ = index_->cell_map_.lower_bound(target);
+ Refresh();
+}
+
+inline std::unique_ptr<MutableS2ShapeIndex::IteratorBase>
+MutableS2ShapeIndex::NewIterator(InitialPosition pos) const {
+ return absl::make_unique<Iterator>(this, pos);
+}
+
+inline bool MutableS2ShapeIndex::is_fresh() const {
+ return index_status_.load(std::memory_order_relaxed) == FRESH;
+}
+
+// Return true if this is the first update to the index.
+inline bool MutableS2ShapeIndex::is_first_update() const {
+ // Note that it is not sufficient to check whether cell_map_ is empty, since
+ // entries are added during the update process.
+ return pending_additions_begin_ == 0;
+}
+
+// Given that the given shape is being updated, return true if it is being
+// removed (as opposed to being added).
+inline bool MutableS2ShapeIndex::is_shape_being_removed(int shape_id) const {
+ // All shape ids being removed are less than all shape ids being added.
+ return shape_id < pending_additions_begin_;
+}
+
+// Ensure that any pending updates have been applied. This method must be
+// called before accessing the cell_map_ field, even if the index_status_
+// appears to be FRESH, because a memory barrier is required in order to
+// ensure that all the index updates are visible if the updates were done in
+// another thread.
+inline void MutableS2ShapeIndex::MaybeApplyUpdates() const {
+ // To avoid acquiring and releasing the spinlock on every query, we use
+ // atomic operations when testing whether the status is FRESH and when
+ // updating the status to be FRESH. This guarantees that any thread that
+ // sees a status of FRESH will also see the corresponding index updates.
+ if (index_status_.load(std::memory_order_acquire) != FRESH) {
+ const_cast<MutableS2ShapeIndex*>(this)->ApplyUpdatesThreadSafe();
+ }
+}
+
+#endif // S2_MUTABLE_S2SHAPE_INDEX_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_R1INTERVAL_H_
+#define S2_R1INTERVAL_H_
+
+#include <algorithm>
+#include <cmath>
+#include <iosfwd>
+#include <iostream>
+
+#include "s2/base/logging.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/util/math/vector.h" // IWYU pragma: export
+
+// An R1Interval represents a closed, bounded interval on the real line.
+// It is capable of representing the empty interval (containing no points)
+// and zero-length intervals (containing a single point).
+//
+// This class is intended to be copied by value as desired. It uses
+// the default copy constructor and assignment operator.
+class R1Interval {
+ public:
+ // Constructor. If lo > hi, the interval is empty.
+ R1Interval(double lo, double hi) : bounds_(lo, hi) {}
+
+ // The default constructor creates an empty interval. (Any interval where
+ // lo > hi is considered to be empty.)
+ //
+ // Note: Don't construct an interval using the default constructor and
+ // set_lo()/set_hi(), since this technique doesn't work with S1Interval and
+ // is bad programming style anyways. If you need to set both endpoints, use
+ // the constructor above:
+ //
+ // lat_bounds_ = R1Interval(lat_lo, lat_hi);
+ R1Interval() : bounds_(1, 0) {}
+
+ // Returns an empty interval.
+ static R1Interval Empty() { return R1Interval(); }
+
+ // Convenience method to construct an interval containing a single point.
+ static R1Interval FromPoint(double p) { return R1Interval(p, p); }
+
+ // Convenience method to construct the minimal interval containing
+ // the two given points. This is equivalent to starting with an empty
+ // interval and calling AddPoint() twice, but it is more efficient.
+ static R1Interval FromPointPair(double p1, double p2) {
+ if (p1 <= p2) {
+ return R1Interval(p1, p2);
+ } else {
+ return R1Interval(p2, p1);
+ }
+ }
+
+ // Accessors methods.
+ double lo() const { return bounds_[0]; }
+ double hi() const { return bounds_[1]; }
+
+ // Methods to modify one endpoint of an existing R1Interval. Do not use
+ // these methods if you want to replace both endpoints of the interval; use
+ // a constructor instead. For example:
+ //
+ // *lat_bounds = R1Interval(lat_lo, lat_hi);
+ void set_lo(double p) { bounds_[0] = p; }
+ void set_hi(double p) { bounds_[1] = p; }
+
+ // Methods that allow the R1Interval to be accessed as a vector. (The
+ // recommended style is to use lo() and hi() whenever possible, but these
+ // methods are useful when the endpoint to be selected is not constant.)
+ double operator[](int i) const { return bounds_[i]; }
+ double& operator[](int i) { return bounds_[i]; }
+ const Vector2_d& bounds() const { return bounds_; }
+ Vector2_d* mutable_bounds() { return &bounds_; }
+
+ // Return true if the interval is empty, i.e. it contains no points.
+ bool is_empty() const { return lo() > hi(); }
+
+ // Return the center of the interval. For empty intervals,
+ // the result is arbitrary.
+ double GetCenter() const { return 0.5 * (lo() + hi()); }
+
+ // Return the length of the interval. The length of an empty interval
+ // is negative.
+ double GetLength() const { return hi() - lo(); }
+
+ bool Contains(double p) const {
+ return p >= lo() && p <= hi();
+ }
+
+ bool InteriorContains(double p) const {
+ return p > lo() && p < hi();
+ }
+
+ // Return true if this interval contains the interval 'y'.
+ bool Contains(const R1Interval& y) const {
+ if (y.is_empty()) return true;
+ return y.lo() >= lo() && y.hi() <= hi();
+ }
+
+ // Return true if the interior of this interval contains the entire
+ // interval 'y' (including its boundary).
+ bool InteriorContains(const R1Interval& y) const {
+ if (y.is_empty()) return true;
+ return y.lo() > lo() && y.hi() < hi();
+ }
+
+ // Return true if this interval intersects the given interval,
+ // i.e. if they have any points in common.
+ bool Intersects(const R1Interval& y) const {
+ if (lo() <= y.lo()) {
+ return y.lo() <= hi() && y.lo() <= y.hi();
+ } else {
+ return lo() <= y.hi() && lo() <= hi();
+ }
+ }
+
+ // Return true if the interior of this interval intersects
+ // any point of the given interval (including its boundary).
+ bool InteriorIntersects(const R1Interval& y) const {
+ return y.lo() < hi() && lo() < y.hi() && lo() < hi() && y.lo() <= y.hi();
+ }
+
+ // Return the Hausdorff distance to the given interval 'y'. For two
+ // R1Intervals x and y, this distance is defined as
+ // h(x, y) = max_{p in x} min_{q in y} d(p, q).
+ double GetDirectedHausdorffDistance(const R1Interval& y) const {
+ if (is_empty()) return 0.0;
+ if (y.is_empty()) return HUGE_VAL;
+ return std::max(0.0, std::max(hi() - y.hi(), y.lo() - lo()));
+ }
+
+ // Expand the interval so that it contains the given point "p".
+ void AddPoint(double p) {
+ if (is_empty()) { set_lo(p); set_hi(p); }
+ else if (p < lo()) { set_lo(p); } // NOLINT
+ else if (p > hi()) { set_hi(p); } // NOLINT
+ }
+
+ // Expand the interval so that it contains the given interval "y".
+ void AddInterval(const R1Interval& y) {
+ if (y.is_empty()) return;
+ if (is_empty()) { *this = y; return; }
+ if (y.lo() < lo()) set_lo(y.lo());
+ if (y.hi() > hi()) set_hi(y.hi());
+ }
+
+ // Return the closest point in the interval to the given point "p".
+ // The interval must be non-empty.
+ double Project(double p) const {
+ S2_DCHECK(!is_empty());
+ return std::max(lo(), std::min(hi(), p));
+ }
+
+ // Return an interval that has been expanded on each side by the given
+ // distance "margin". If "margin" is negative, then shrink the interval on
+ // each side by "margin" instead. The resulting interval may be empty. Any
+ // expansion of an empty interval remains empty.
+ R1Interval Expanded(double margin) const {
+ if (is_empty()) return *this;
+ return R1Interval(lo() - margin, hi() + margin);
+ }
+
+ // Return the smallest interval that contains this interval and the
+ // given interval "y".
+ R1Interval Union(const R1Interval& y) const {
+ if (is_empty()) return y;
+ if (y.is_empty()) return *this;
+ return R1Interval(std::min(lo(), y.lo()), std::max(hi(), y.hi()));
+ }
+
+ // Return the intersection of this interval with the given interval.
+ // Empty intervals do not need to be special-cased.
+ R1Interval Intersection(const R1Interval& y) const {
+ return R1Interval(std::max(lo(), y.lo()), std::min(hi(), y.hi()));
+ }
+
+ // Return true if two intervals contain the same set of points.
+ bool operator==(const R1Interval& y) const {
+ return (lo() == y.lo() && hi() == y.hi()) || (is_empty() && y.is_empty());
+ }
+
+ // Return true if two intervals do not contain the same set of points.
+ bool operator!=(const R1Interval& y) const {
+ return !operator==(y);
+ }
+
+ // Return true if this interval can be transformed into the given interval
+ // by moving each endpoint by at most "max_error". The empty interval is
+ // considered to be positioned arbitrarily on the real line, thus any
+ // interval with (length <= 2*max_error) matches the empty interval.
+ bool ApproxEquals(const R1Interval& y, double max_error = 1e-15) const {
+ if (is_empty()) return y.GetLength() <= 2 * max_error;
+ if (y.is_empty()) return GetLength() <= 2 * max_error;
+ return (std::fabs(y.lo() - lo()) <= max_error &&
+ std::fabs(y.hi() - hi()) <= max_error);
+ }
+
+ private:
+ Vector2_d bounds_;
+};
+
+inline std::ostream& operator<<(std::ostream& os, const R1Interval& x) {
+ return os << "[" << x.lo() << ", " << x.hi() << "]";
+}
+
+#endif // S2_R1INTERVAL_H_
--- /dev/null
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_R2_H_
+#define S2_R2_H_
+
+#include "s2/_fp_contract_off.h"
+#include "s2/util/math/vector.h" // IWYU pragma: export
+
+using R2Point = Vector2_d;
+
+#endif // S2_R2_H_
--- /dev/null
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_R2RECT_H_
+#define S2_R2RECT_H_
+
+#include <iosfwd>
+
+#include "s2/base/logging.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/r1interval.h"
+#include "s2/r2.h"
+
+// An R2Rect represents a closed axis-aligned rectangle in the (x,y) plane.
+//
+// This class is intended to be copied by value as desired. It uses
+// the default copy constructor and assignment operator, however it is
+// not a "plain old datatype" (POD) because it has virtual functions.
+class R2Rect {
+ public:
+ // Construct a rectangle from the given lower-left and upper-right points.
+ R2Rect(const R2Point& lo, const R2Point& hi);
+
+ // Construct a rectangle from the given intervals in x and y. The two
+ // intervals must either be both empty or both non-empty.
+ R2Rect(const R1Interval& x, const R1Interval& y);
+
+ // The default constructor creates an empty R2Rect.
+ R2Rect();
+
+ // The canonical empty rectangle. Use is_empty() to test for empty
+ // rectangles, since they have more than one representation.
+ static R2Rect Empty();
+
+ // Construct a rectangle from a center point and size in each dimension.
+ // Both components of size should be non-negative, i.e. this method cannot
+ // be used to create an empty rectangle.
+ static R2Rect FromCenterSize(const R2Point& center, const R2Point& size);
+
+ // Convenience method to construct a rectangle containing a single point.
+ static R2Rect FromPoint(const R2Point& p);
+
+ // Convenience method to construct the minimal bounding rectangle containing
+ // the two given points. This is equivalent to starting with an empty
+ // rectangle and calling AddPoint() twice. Note that it is different than
+ // the R2Rect(lo, hi) constructor, where the first point is always
+ // used as the lower-left corner of the resulting rectangle.
+ static R2Rect FromPointPair(const R2Point& p1, const R2Point& p2);
+
+ // Accessor methods.
+ const R1Interval& x() const { return bounds_[0]; }
+ const R1Interval& y() const { return bounds_[1]; }
+ R2Point lo() const { return R2Point(x().lo(), y().lo()); }
+ R2Point hi() const { return R2Point(x().hi(), y().hi()); }
+
+ // Methods that allow the R2Rect to be accessed as a vector.
+ const R1Interval& operator[](int i) const { return bounds_[i]; }
+ R1Interval& operator[](int i) { return bounds_[i]; }
+
+ // Return true if the rectangle is valid, which essentially just means
+ // that if the bound for either axis is empty then both must be.
+ bool is_valid() const;
+
+ // Return true if the rectangle is empty, i.e. it contains no points at all.
+ bool is_empty() const;
+
+ // Return the k-th vertex of the rectangle (k = 0,1,2,3) in CCW order.
+ // Vertex 0 is in the lower-left corner. For convenience, the argument is
+ // reduced modulo 4 to the range [0..3].
+ R2Point GetVertex(int k) const;
+
+ // Return the vertex in direction "i" along the x-axis (0=left, 1=right) and
+ // direction "j" along the y-axis (0=down, 1=up). Equivalently, return the
+ // vertex constructed by selecting endpoint "i" of the x-interval (0=lo,
+ // 1=hi) and vertex "j" of the y-interval.
+ R2Point GetVertex(int i, int j) const;
+
+ // Return the center of the rectangle in (x,y)-space.
+ R2Point GetCenter() const;
+
+ // Return the width and height of this rectangle in (x,y)-space. Empty
+ // rectangles have a negative width and height.
+ R2Point GetSize() const;
+
+ // Return true if the rectangle contains the given point. Note that
+ // rectangles are closed regions, i.e. they contain their boundary.
+ bool Contains(const R2Point& p) const;
+
+ // Return true if and only if the given point is contained in the interior
+ // of the region (i.e. the region excluding its boundary).
+ bool InteriorContains(const R2Point& p) const;
+
+ // Return true if and only if the rectangle contains the given other
+ // rectangle.
+ bool Contains(const R2Rect& other) const;
+
+ // Return true if and only if the interior of this rectangle contains all
+ // points of the given other rectangle (including its boundary).
+ bool InteriorContains(const R2Rect& other) const;
+
+ // Return true if this rectangle and the given other rectangle have any
+ // points in common.
+ bool Intersects(const R2Rect& other) const;
+
+ // Return true if and only if the interior of this rectangle intersects
+ // any point (including the boundary) of the given other rectangle.
+ bool InteriorIntersects(const R2Rect& other) const;
+
+ // Expand the rectangle to include the given point. The rectangle is
+ // expanded by the minimum amount possible.
+ void AddPoint(const R2Point& p);
+
+ // Expand the rectangle to include the given other rectangle. This is the
+ // same as replacing the rectangle by the union of the two rectangles, but
+ // is somewhat more efficient.
+ void AddRect(const R2Rect& other);
+
+ // Return the closest point in the rectangle to the given point "p".
+ // The rectangle must be non-empty.
+ R2Point Project(const R2Point& p) const;
+
+ // Return a rectangle that has been expanded on each side in the x-direction
+ // by margin.x(), and on each side in the y-direction by margin.y(). If
+ // either margin is empty, then shrink the interval on the corresponding
+ // sides instead. The resulting rectangle may be empty. Any expansion of
+ // an empty rectangle remains empty.
+ R2Rect Expanded(const R2Point& margin) const;
+ R2Rect Expanded(double margin) const;
+
+ // Return the smallest rectangle containing the union of this rectangle and
+ // the given rectangle.
+ R2Rect Union(const R2Rect& other) const;
+
+ // Return the smallest rectangle containing the intersection of this
+ // rectangle and the given rectangle.
+ R2Rect Intersection(const R2Rect& other) const;
+
+ // Return true if two rectangles contains the same set of points.
+ bool operator==(const R2Rect& other) const;
+
+ // Return true if the x- and y-intervals of the two rectangles are the same
+ // up to the given tolerance (see r1interval.h for details).
+ bool ApproxEquals(const R2Rect& other, double max_error = 1e-15) const;
+
+ private:
+ R1Interval bounds_[2];
+};
+
+inline R2Rect::R2Rect(const R2Point& lo, const R2Point& hi) {
+ bounds_[0] = R1Interval(lo.x(), hi.x());
+ bounds_[1] = R1Interval(lo.y(), hi.y());
+ S2_DCHECK(is_valid());
+}
+
+inline R2Rect::R2Rect(const R1Interval& x, const R1Interval& y) {
+ bounds_[0] = x;
+ bounds_[1] = y;
+ S2_DCHECK(is_valid());
+}
+
+inline R2Rect::R2Rect() {
+ // The default R1Interval constructor creates an empty interval.
+ S2_DCHECK(is_valid());
+}
+
+inline R2Rect R2Rect::Empty() {
+ return R2Rect(R1Interval::Empty(), R1Interval::Empty());
+}
+
+inline bool R2Rect::is_valid() const {
+ // The x/y ranges must either be both empty or both non-empty.
+ return x().is_empty() == y().is_empty();
+}
+
+inline bool R2Rect::is_empty() const {
+ return x().is_empty();
+}
+
+inline R2Rect R2Rect::FromPoint(const R2Point& p) {
+ return R2Rect(p, p);
+}
+
+inline R2Point R2Rect::GetVertex(int k) const {
+ // Twiddle bits to return the points in CCW order (lower left, lower right,
+ // upper right, upper left).
+ int j = (k >> 1) & 1;
+ return GetVertex(j ^ (k & 1), j);
+}
+
+inline R2Point R2Rect::GetVertex(int i, int j) const {
+ return R2Point(bounds_[0][i], bounds_[1][j]);
+}
+
+inline R2Point R2Rect::GetCenter() const {
+ return R2Point(x().GetCenter(), y().GetCenter());
+}
+
+inline R2Point R2Rect::GetSize() const {
+ return R2Point(x().GetLength(), y().GetLength());
+}
+
+inline bool R2Rect::Contains(const R2Point& p) const {
+ return x().Contains(p.x()) && y().Contains(p.y());
+}
+
+inline bool R2Rect::InteriorContains(const R2Point& p) const {
+ return x().InteriorContains(p.x()) && y().InteriorContains(p.y());
+}
+
+inline R2Rect R2Rect::Expanded(double margin) const {
+ return Expanded(R2Point(margin, margin));
+}
+
+inline bool R2Rect::operator==(const R2Rect& other) const {
+ return x() == other.x() && y() == other.y();
+}
+
+std::ostream& operator<<(std::ostream& os, const R2Rect& r);
+
+#endif // S2_R2RECT_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S1ANGLE_H_
+#define S2_S1ANGLE_H_
+
+#include <cmath>
+#include <limits>
+#include <ostream>
+#include <type_traits>
+
+#include "s2/base/integral_types.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/s2point.h"
+#include "s2/util/math/mathutil.h"
+#include "s2/util/math/vector.h"
+
+class S2LatLng;
+
+#ifndef SWIG
+#define IFNDEF_SWIG(x) x
+#else
+#define IFNDEF_SWIG(x)
+#endif
+
+// This class represents a one-dimensional angle (as opposed to a
+// two-dimensional solid angle). It has methods for converting angles to
+// or from radians, degrees, and the E5/E6/E7 representations (i.e. degrees
+// multiplied by 1e5/1e6/1e7 and rounded to the nearest integer).
+//
+// The internal representation is a double-precision value in radians, so
+// conversion to and from radians is exact. Conversions between E5, E6, E7,
+// and Degrees are not always exact; for example, Degrees(3.1) is different
+// from E6(3100000) or E7(310000000). However, the following properties are
+// guaranteed for any integer "n", provided that "n" is in the input range of
+// both functions:
+//
+// Degrees(n) == E6(1000000 * n)
+// Degrees(n) == E7(10000000 * n)
+// E6(n) == E7(10 * n)
+//
+// The corresponding properties are *not* true for E5, so if you use E5 then
+// don't test for exact equality when comparing to other formats such as
+// Degrees or E7.
+//
+// The following conversions between degrees and radians are exact:
+//
+// Degrees(180) == Radians(M_PI)
+// Degrees(45 * k) == Radians(k * M_PI / 4) for k == 0..8
+//
+// These identities also hold when the arguments are scaled up or down by any
+// power of 2. Some similar identities are also true, for example,
+// Degrees(60) == Radians(M_PI / 3), but be aware that this type of identity
+// does not hold in general. For example, Degrees(3) != Radians(M_PI / 60).
+//
+// Similarly, the conversion to radians means that Angle::Degrees(x).degrees()
+// does not always equal "x". For example,
+//
+// S1Angle::Degrees(45 * k).degrees() == 45 * k for k == 0..8
+// but S1Angle::Degrees(60).degrees() != 60.
+//
+// This means that when testing for equality, you should allow for numerical
+// errors (EXPECT_DOUBLE_EQ) or convert to discrete E5/E6/E7 values first.
+//
+// CAVEAT: All of the above properties depend on "double" being the usual
+// 64-bit IEEE 754 type (which is true on almost all modern platforms).
+//
+// This class is intended to be copied by value as desired. It uses
+// the default copy constructor and assignment operator.
+class S1Angle {
+ public:
+ // These methods construct S1Angle objects from their measure in radians
+ // or degrees.
+ static constexpr S1Angle Radians(double radians);
+ static constexpr S1Angle Degrees(double degrees);
+ static constexpr S1Angle E5(int32 e5);
+ static constexpr S1Angle E6(int32 e6);
+ static constexpr S1Angle E7(int32 e7);
+
+ // Convenience functions -- to use when args have been fixed32s in protos.
+ //
+ // The arguments are static_cast into int32, so very large unsigned values
+ // are treated as negative numbers.
+ static constexpr S1Angle UnsignedE6(uint32 e6);
+ static constexpr S1Angle UnsignedE7(uint32 e7);
+
+ // The default constructor yields a zero angle. This is useful for STL
+ // containers and class methods with output arguments.
+ IFNDEF_SWIG(constexpr) S1Angle() : radians_(0) {}
+
+ // Return an angle larger than any finite angle.
+ static constexpr S1Angle Infinity();
+
+ // A explicit shorthand for the default constructor.
+ static constexpr S1Angle Zero();
+
+ // Return the angle between two points, which is also equal to the distance
+ // between these points on the unit sphere. The points do not need to be
+ // normalized. This function has a maximum error of 3.25 * DBL_EPSILON (or
+ // 2.5 * DBL_EPSILON for angles up to 1 radian). If either point is
+ // zero-length (e.g. an uninitialized S2Point), or almost zero-length, the
+ // resulting angle will be zero.
+ S1Angle(const S2Point& x, const S2Point& y);
+
+ // Like the constructor above, but return the angle (i.e., distance) between
+ // two S2LatLng points. This function has about 15 digits of accuracy for
+ // small distances but only about 8 digits of accuracy as the distance
+ // approaches 180 degrees (i.e., nearly-antipodal points).
+ S1Angle(const S2LatLng& x, const S2LatLng& y);
+
+ constexpr double radians() const;
+ constexpr double degrees() const;
+
+ int32 e5() const;
+ int32 e6() const;
+ int32 e7() const;
+
+ // Return the absolute value of an angle.
+ S1Angle abs() const;
+
+ // Comparison operators.
+ friend bool operator==(S1Angle x, S1Angle y);
+ friend bool operator!=(S1Angle x, S1Angle y);
+ friend bool operator<(S1Angle x, S1Angle y);
+ friend bool operator>(S1Angle x, S1Angle y);
+ friend bool operator<=(S1Angle x, S1Angle y);
+ friend bool operator>=(S1Angle x, S1Angle y);
+
+ // Simple arithmetic operators for manipulating S1Angles.
+ friend S1Angle operator-(S1Angle a);
+ friend S1Angle operator+(S1Angle a, S1Angle b);
+ friend S1Angle operator-(S1Angle a, S1Angle b);
+ friend S1Angle operator*(double m, S1Angle a);
+ friend S1Angle operator*(S1Angle a, double m);
+ friend S1Angle operator/(S1Angle a, double m);
+ friend double operator/(S1Angle a, S1Angle b);
+ S1Angle& operator+=(S1Angle a);
+ S1Angle& operator-=(S1Angle a);
+ S1Angle& operator*=(double m);
+ S1Angle& operator/=(double m);
+
+ // Trigonmetric functions (not necessary but slightly more convenient).
+ friend double sin(S1Angle a);
+ friend double cos(S1Angle a);
+ friend double tan(S1Angle a);
+
+ // Return the angle normalized to the range (-180, 180] degrees.
+ S1Angle Normalized() const;
+
+ // Normalize this angle to the range (-180, 180] degrees.
+ void Normalize();
+
+ // When S1Angle is used as a key in one of the btree container types
+ // (util/btree), indicate that linear rather than binary search should be
+ // used. This is much faster when the comparison function is cheap.
+ typedef std::true_type goog_btree_prefer_linear_node_search;
+
+ private:
+ explicit IFNDEF_SWIG(constexpr) S1Angle(double radians) : radians_(radians) {}
+ double radians_;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline constexpr S1Angle S1Angle::Infinity() {
+ return S1Angle(std::numeric_limits<double>::infinity());
+}
+
+inline constexpr S1Angle S1Angle::Zero() {
+ return S1Angle(0);
+}
+
+inline constexpr double S1Angle::radians() const {
+ return radians_;
+}
+
+inline constexpr double S1Angle::degrees() const {
+ return (180 / M_PI) * radians_;
+}
+
+// Note that the E5, E6, and E7 conversion involve two multiplications rather
+// than one. This is mainly for backwards compatibility (changing this would
+// break many tests), but it does have the nice side effect that conversions
+// between Degrees, E6, and E7 are exact when the arguments are integers.
+
+inline int32 S1Angle::e5() const {
+ return MathUtil::FastIntRound(1e5 * degrees());
+}
+
+inline int32 S1Angle::e6() const {
+ return MathUtil::FastIntRound(1e6 * degrees());
+}
+
+inline int32 S1Angle::e7() const {
+ return MathUtil::FastIntRound(1e7 * degrees());
+}
+
+inline S1Angle S1Angle::abs() const {
+ return S1Angle(std::fabs(radians_));
+}
+
+inline bool operator==(S1Angle x, S1Angle y) {
+ return x.radians() == y.radians();
+}
+
+inline bool operator!=(S1Angle x, S1Angle y) {
+ return x.radians() != y.radians();
+}
+
+inline bool operator<(S1Angle x, S1Angle y) {
+ return x.radians() < y.radians();
+}
+
+inline bool operator>(S1Angle x, S1Angle y) {
+ return x.radians() > y.radians();
+}
+
+inline bool operator<=(S1Angle x, S1Angle y) {
+ return x.radians() <= y.radians();
+}
+
+inline bool operator>=(S1Angle x, S1Angle y) {
+ return x.radians() >= y.radians();
+}
+
+inline S1Angle operator-(S1Angle a) {
+ return S1Angle::Radians(-a.radians());
+}
+
+inline S1Angle operator+(S1Angle a, S1Angle b) {
+ return S1Angle::Radians(a.radians() + b.radians());
+}
+
+inline S1Angle operator-(S1Angle a, S1Angle b) {
+ return S1Angle::Radians(a.radians() - b.radians());
+}
+
+inline S1Angle operator*(double m, S1Angle a) {
+ return S1Angle::Radians(m * a.radians());
+}
+
+inline S1Angle operator*(S1Angle a, double m) {
+ return S1Angle::Radians(m * a.radians());
+}
+
+inline S1Angle operator/(S1Angle a, double m) {
+ return S1Angle::Radians(a.radians() / m);
+}
+
+inline double operator/(S1Angle a, S1Angle b) {
+ return a.radians() / b.radians();
+}
+
+inline S1Angle& S1Angle::operator+=(S1Angle a) {
+ radians_ += a.radians();
+ return *this;
+}
+
+inline S1Angle& S1Angle::operator-=(S1Angle a) {
+ radians_ -= a.radians();
+ return *this;
+}
+
+inline S1Angle& S1Angle::operator*=(double m) {
+ radians_ *= m;
+ return *this;
+}
+
+inline S1Angle& S1Angle::operator/=(double m) {
+ radians_ /= m;
+ return *this;
+}
+
+inline double sin(S1Angle a) {
+ return sin(a.radians());
+}
+
+inline double cos(S1Angle a) {
+ return cos(a.radians());
+}
+
+inline double tan(S1Angle a) {
+ return tan(a.radians());
+}
+
+inline constexpr S1Angle S1Angle::Radians(double radians) {
+ return S1Angle(radians);
+}
+
+inline constexpr S1Angle S1Angle::Degrees(double degrees) {
+ return S1Angle((M_PI / 180) * degrees);
+}
+
+inline constexpr S1Angle S1Angle::E5(int32 e5) {
+ return Degrees(1e-5 * e5);
+}
+
+inline constexpr S1Angle S1Angle::E6(int32 e6) {
+ return Degrees(1e-6 * e6);
+}
+
+inline constexpr S1Angle S1Angle::E7(int32 e7) {
+ return Degrees(1e-7 * e7);
+}
+
+inline constexpr S1Angle S1Angle::UnsignedE6(uint32 e6) {
+ return E6(static_cast<int32>(e6));
+}
+
+inline constexpr S1Angle S1Angle::UnsignedE7(uint32 e7) {
+ return E7(static_cast<int32>(e7));
+}
+
+// Writes the angle in degrees with 7 digits of precision after the
+// decimal point, e.g. "17.3745904".
+std::ostream& operator<<(std::ostream& os, S1Angle a);
+
+#undef IFNDEF_SWIG
+
+#endif // S2_S1ANGLE_H_
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S1CHORD_ANGLE_H_
+#define S2_S1CHORD_ANGLE_H_
+
+#include <cmath>
+#include <limits>
+#include <ostream>
+#include <type_traits>
+
+#include "s2/_fp_contract_off.h"
+#include "s2/s1angle.h"
+#include "s2/s2pointutil.h"
+
+// S1ChordAngle represents the angle subtended by a chord (i.e., the straight
+// line segment connecting two points on the sphere). Its representation
+// makes it very efficient for computing and comparing distances, but unlike
+// S1Angle it is only capable of representing angles between 0 and Pi radians.
+// Generally, S1ChordAngle should only be used in loops where many angles need
+// to be calculated and compared. Otherwise it is simpler to use S1Angle.
+//
+// S1ChordAngle also loses some accuracy as the angle approaches Pi radians.
+// Specifically, the representation of (Pi - x) radians has an error of about
+// (1e-15 / x), with a maximum error of about 2e-8 radians (about 13cm on the
+// Earth's surface). For comparison, for angles up to 90 degrees (10000km)
+// the worst-case representation error is about 2e-16 radians (1 nanometer),
+// which is about the same as S1Angle.
+//
+// This class is intended to be copied by value as desired. It uses
+// the default copy constructor and assignment operator.
+class S1ChordAngle {
+ public:
+ // The default constructor yields a zero angle. This is useful for STL
+ // containers and class methods with output arguments.
+ S1ChordAngle() : length2_(0) {}
+
+ // Construct the S1ChordAngle corresponding to the distance between the two
+ // given points. The points must be unit length.
+ S1ChordAngle(const S2Point& x, const S2Point& y);
+
+ // Return the zero chord angle.
+ static S1ChordAngle Zero();
+
+ // Return a chord angle of 90 degrees (a "right angle").
+ static S1ChordAngle Right();
+
+ // Return a chord angle of 180 degrees (a "straight angle"). This is the
+ // maximum finite chord angle.
+ static S1ChordAngle Straight();
+
+ // Return a chord angle larger than any finite chord angle. The only valid
+ // operations on Infinity() are comparisons, S1Angle conversions, and
+ // Successor() / Predecessor().
+ static S1ChordAngle Infinity();
+
+ // Return a chord angle smaller than Zero(). The only valid operations on
+ // Negative() are comparisons, S1Angle conversions, and Successor() /
+ // Predecessor().
+ static S1ChordAngle Negative();
+
+ // Conversion from an S1Angle. Angles outside the range [0, Pi] are handled
+ // as follows: Infinity() is mapped to Infinity(), negative angles are
+ // mapped to Negative(), and finite angles larger than Pi are mapped to
+ // Straight().
+ //
+ // Note that this operation is relatively expensive and should be avoided.
+ // To use S1ChordAngle effectively, you should structure your code so that
+ // input arguments are converted to S1ChordAngles at the beginning of your
+ // algorithm, and results are converted back to S1Angles only at the end.
+ explicit S1ChordAngle(S1Angle angle);
+
+ // Convenience methods implemented by converting from an S1Angle.
+ static S1ChordAngle Radians(double radians);
+ static S1ChordAngle Degrees(double degrees);
+ static S1ChordAngle E5(int32 e5);
+ static S1ChordAngle E6(int32 e6);
+ static S1ChordAngle E7(int32 e7);
+
+ // Construct an S1ChordAngle that is an upper bound on the given S1Angle,
+ // i.e. such that FastUpperBoundFrom(x).ToAngle() >= x. Unlike the S1Angle
+ // constructor above, this method is very fast, and the bound is accurate to
+ // within 1% for distances up to about 3100km on the Earth's surface.
+ static S1ChordAngle FastUpperBoundFrom(S1Angle angle);
+
+ // Construct an S1ChordAngle from the squared chord length. Note that the
+ // argument is automatically clamped to a maximum of 4.0 to handle possible
+ // roundoff errors. The argument must be non-negative.
+ static S1ChordAngle FromLength2(double length2);
+
+ // Converts to an S1Angle. Can be used just like an S1Angle constructor:
+ //
+ // S1ChordAngle x = ...;
+ // return S1Angle(x);
+ //
+ // Infinity() is converted to S1Angle::Infinity(), and Negative() is
+ // converted to an unspecified negative S1Angle.
+ //
+ // Note that the conversion uses trigonometric functions and therefore
+ // should be avoided in inner loops.
+ explicit operator S1Angle() const;
+
+ // Converts to an S1Angle (equivalent to the operator above).
+ S1Angle ToAngle() const;
+
+ // Convenience methods implemented by calling ToAngle() first. Note that
+ // because of the S1Angle conversion these methods are relatively expensive
+ // (despite their lowercase names), so the results should be cached if they
+ // are needed inside loops.
+ double radians() const;
+ double degrees() const;
+ int32 e5() const;
+ int32 e6() const;
+ int32 e7() const;
+
+ // All operators and functions are declared here so that we can put them all
+ // in one place. (The compound assignment operators must be put here.)
+
+ // Comparison operators.
+ friend bool operator==(S1ChordAngle x, S1ChordAngle y);
+ friend bool operator!=(S1ChordAngle x, S1ChordAngle y);
+ friend bool operator<(S1ChordAngle x, S1ChordAngle y);
+ friend bool operator>(S1ChordAngle x, S1ChordAngle y);
+ friend bool operator<=(S1ChordAngle x, S1ChordAngle y);
+ friend bool operator>=(S1ChordAngle x, S1ChordAngle y);
+
+ // Comparison predicates.
+ bool is_zero() const;
+ bool is_negative() const;
+ bool is_infinity() const;
+ bool is_special() const; // Negative or infinity.
+
+ // Only addition and subtraction of S1ChordAngles is supported. These
+ // methods add or subtract the corresponding S1Angles, and clamp the result
+ // to the range [0, Pi]. Both arguments must be non-negative and
+ // non-infinite.
+ //
+ // REQUIRES: !a.is_special() && !b.is_special()
+ friend S1ChordAngle operator+(S1ChordAngle a, S1ChordAngle b);
+ friend S1ChordAngle operator-(S1ChordAngle a, S1ChordAngle b);
+ S1ChordAngle& operator+=(S1ChordAngle a);
+ S1ChordAngle& operator-=(S1ChordAngle a);
+
+ // Trigonmetric functions. It is more accurate and efficient to call these
+ // rather than first converting to an S1Angle.
+ friend double sin(S1ChordAngle a);
+ friend double cos(S1ChordAngle a);
+ friend double tan(S1ChordAngle a);
+
+ // Returns sin(a)**2, but computed more efficiently.
+ friend double sin2(S1ChordAngle a);
+
+ // The squared length of the chord. (Most clients will not need this.)
+ double length2() const { return length2_; }
+
+ // Returns the smallest representable S1ChordAngle larger than this object.
+ // This can be used to convert a "<" comparison to a "<=" comparison. For
+ // example:
+ //
+ // S2ClosestEdgeQuery query(...);
+ // S1ChordAngle limit = ...;
+ // if (query.IsDistanceLess(target, limit.Successor())) {
+ // // Distance to "target" is less than or equal to "limit".
+ // }
+ //
+ // Note the following special cases:
+ // Negative().Successor() == Zero()
+ // Straight().Successor() == Infinity()
+ // Infinity().Successor() == Infinity()
+ S1ChordAngle Successor() const;
+
+ // Like Successor(), but returns the largest representable S1ChordAngle less
+ // than this object.
+ //
+ // Note the following special cases:
+ // Infinity().Predecessor() == Straight()
+ // Zero().Predecessor() == Negative()
+ // Negative().Predecessor() == Negative()
+ S1ChordAngle Predecessor() const;
+
+ // Returns a new S1ChordAngle that has been adjusted by the given error
+ // bound (which can be positive or negative). "error" should be the value
+ // returned by one of the error bound methods below. For example:
+ // S1ChordAngle a(x, y);
+ // S1ChordAngle a1 = a.PlusError(a.GetS2PointConstructorMaxError());
+ S1ChordAngle PlusError(double error) const;
+
+ // Return the maximum error in length2() for the S1ChordAngle(x, y)
+ // constructor, assuming that "x" and "y" are normalized to within the
+ // bounds guaranteed by S2Point::Normalize(). (The error is defined with
+ // respect to the true distance after the points are projected to lie
+ // exactly on the sphere.)
+ double GetS2PointConstructorMaxError() const;
+
+ // Return the maximum error in length2() for the S1Angle constructor.
+ double GetS1AngleConstructorMaxError() const;
+
+ // Return true if the internal representation is valid. Negative() and
+ // Infinity() are both considered valid.
+ bool is_valid() const;
+
+ // When S1ChordAngle is used as a key in one of the btree container types
+ // (util/btree), indicate that linear rather than binary search should be
+ // used. This is much faster when the comparison function is cheap.
+ typedef std::true_type goog_btree_prefer_linear_node_search;
+
+ private:
+ // S1ChordAngles are represented by the squared chord length, which can
+ // range from 0 to 4. Infinity() uses an infinite squared length.
+ explicit S1ChordAngle(double length2) : length2_(length2) {
+ S2_DCHECK(is_valid());
+ }
+ double length2_;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline S1ChordAngle::S1ChordAngle(const S2Point& x, const S2Point& y) {
+ S2_DCHECK(S2::IsUnitLength(x));
+ S2_DCHECK(S2::IsUnitLength(y));
+ // The squared distance may slightly exceed 4.0 due to roundoff errors.
+ // The maximum error in the result is 2 * DBL_EPSILON * length2_.
+ length2_ = std::min(4.0, (x - y).Norm2());
+ S2_DCHECK(is_valid());
+}
+
+inline S1ChordAngle S1ChordAngle::FromLength2(double length2) {
+ return S1ChordAngle(std::min(4.0, length2));
+}
+
+inline S1ChordAngle S1ChordAngle::Zero() {
+ return S1ChordAngle(0);
+}
+
+inline S1ChordAngle S1ChordAngle::Right() {
+ return S1ChordAngle(2);
+}
+
+inline S1ChordAngle S1ChordAngle::Straight() {
+ return S1ChordAngle(4);
+}
+
+inline S1ChordAngle S1ChordAngle::Infinity() {
+ return S1ChordAngle(std::numeric_limits<double>::infinity());
+}
+
+inline S1ChordAngle S1ChordAngle::Negative() {
+ return S1ChordAngle(-1);
+}
+
+inline S1ChordAngle S1ChordAngle::Radians(double radians) {
+ return S1ChordAngle(S1Angle::Radians(radians));
+}
+
+inline S1ChordAngle S1ChordAngle::Degrees(double degrees) {
+ return S1ChordAngle(S1Angle::Degrees(degrees));
+}
+
+inline S1ChordAngle S1ChordAngle::E5(int32 e5) {
+ return S1ChordAngle(S1Angle::E5(e5));
+}
+
+inline S1ChordAngle S1ChordAngle::E6(int32 e6) {
+ return S1ChordAngle(S1Angle::E6(e6));
+}
+
+inline S1ChordAngle S1ChordAngle::E7(int32 e7) {
+ return S1ChordAngle(S1Angle::E7(e7));
+}
+
+inline S1ChordAngle S1ChordAngle::FastUpperBoundFrom(S1Angle angle) {
+ // This method uses the distance along the surface of the sphere as an upper
+ // bound on the distance through the sphere's interior.
+ return S1ChordAngle::FromLength2(angle.radians() * angle.radians());
+}
+
+inline S1ChordAngle::operator S1Angle() const {
+ return ToAngle();
+}
+
+inline double S1ChordAngle::radians() const {
+ return ToAngle().radians();
+}
+
+inline double S1ChordAngle::degrees() const {
+ return ToAngle().degrees();
+}
+
+inline int32 S1ChordAngle::e5() const {
+ return ToAngle().e5();
+}
+
+inline int32 S1ChordAngle::e6() const {
+ return ToAngle().e6();
+}
+
+inline int32 S1ChordAngle::e7() const {
+ return ToAngle().e7();
+}
+
+inline bool S1ChordAngle::is_zero() const {
+ return length2_ == 0;
+}
+
+inline bool S1ChordAngle::is_negative() const {
+ // TODO(ericv): Consider stricter check here -- only allow Negative().
+ return length2_ < 0;
+}
+
+inline bool S1ChordAngle::is_infinity() const {
+ return length2_ == std::numeric_limits<double>::infinity();
+}
+
+inline bool S1ChordAngle::is_special() const {
+ return is_negative() || is_infinity();
+}
+
+inline bool operator==(S1ChordAngle x, S1ChordAngle y) {
+ return x.length2() == y.length2();
+}
+
+inline bool operator!=(S1ChordAngle x, S1ChordAngle y) {
+ return x.length2() != y.length2();
+}
+
+inline bool operator<(S1ChordAngle x, S1ChordAngle y) {
+ return x.length2() < y.length2();
+}
+
+inline bool operator>(S1ChordAngle x, S1ChordAngle y) {
+ return x.length2() > y.length2();
+}
+
+inline bool operator<=(S1ChordAngle x, S1ChordAngle y) {
+ return x.length2() <= y.length2();
+}
+
+inline bool operator>=(S1ChordAngle x, S1ChordAngle y) {
+ return x.length2() >= y.length2();
+}
+
+inline S1ChordAngle& S1ChordAngle::operator+=(S1ChordAngle a) {
+ return (*this = *this + a);
+}
+
+inline S1ChordAngle& S1ChordAngle::operator-=(S1ChordAngle a) {
+ return (*this = *this - a);
+}
+
+// Outputs the chord angle as the equivalent S1Angle.
+std::ostream& operator<<(std::ostream& os, S1ChordAngle a);
+
+#endif // S2_S1CHORD_ANGLE_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S1INTERVAL_H_
+#define S2_S1INTERVAL_H_
+
+#include <cmath>
+#include <iosfwd>
+#include <iostream>
+
+#include "s2/base/logging.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/util/math/vector.h" // IWYU pragma: export
+
+// An S1Interval represents a closed interval on a unit circle (also known
+// as a 1-dimensional sphere). It is capable of representing the empty
+// interval (containing no points), the full interval (containing all
+// points), and zero-length intervals (containing a single point).
+//
+// Points are represented by the angle they make with the positive x-axis in
+// the range [-Pi, Pi]. An interval is represented by its lower and upper
+// bounds (both inclusive, since the interval is closed). The lower bound may
+// be greater than the upper bound, in which case the interval is "inverted"
+// (i.e. it passes through the point (-1, 0)).
+//
+// Note that the point (-1, 0) has two valid representations, Pi and -Pi.
+// The normalized representation of this point internally is Pi, so that
+// endpoints of normal intervals are in the range (-Pi, Pi]. However, we
+// take advantage of the point -Pi to construct two special intervals:
+// the Full() interval is [-Pi, Pi], and the Empty() interval is [Pi, -Pi].
+//
+// This class is intended to be copied by value as desired. It uses
+// the default copy constructor and assignment operator.
+class S1Interval {
+ public:
+ // Constructor. Both endpoints must be in the range -Pi to Pi inclusive.
+ // The value -Pi is converted internally to Pi except for the Full()
+ // and Empty() intervals.
+ S1Interval(double lo, double hi);
+
+ // The default constructor creates an empty interval.
+ //
+ // Note: Don't construct an interval using the default constructor and
+ // set_lo()/set_hi(). If you need to set both endpoints, use the
+ // constructor above:
+ //
+ // lng_bounds_ = S1Interval(lng_lo, lng_hi);
+ S1Interval();
+
+ // Returns the empty interval.
+ static S1Interval Empty();
+
+ // Returns the full interval.
+ static S1Interval Full();
+
+ // Convenience method to construct an interval containing a single point.
+ static S1Interval FromPoint(double p);
+
+ // Convenience method to construct the minimal interval containing
+ // the two given points. This is equivalent to starting with an empty
+ // interval and calling AddPoint() twice, but it is more efficient.
+ static S1Interval FromPointPair(double p1, double p2);
+
+ // Accessors methods.
+ double lo() const { return bounds_[0]; }
+ double hi() const { return bounds_[1]; }
+
+ // Methods that allow the S1Interval to be accessed as a vector. (The
+ // recommended style is to use lo() and hi() whenever possible, but these
+ // methods are useful when the endpoint to be selected is not constant.)
+ //
+ // Only const versions of these methods are provided, since S1Interval
+ // has invariants that must be maintained after each update.
+ double operator[](int i) const { return bounds_[i]; }
+ const Vector2_d& bounds() const { return bounds_; }
+
+ // An interval is valid if neither bound exceeds Pi in absolute value,
+ // and the value -Pi appears only in the Empty() and Full() intervals.
+ bool is_valid() const;
+
+ // Return true if the interval contains all points on the unit circle.
+ bool is_full() const { return lo() == -M_PI && hi() == M_PI; }
+
+ // Return true if the interval is empty, i.e. it contains no points.
+ bool is_empty() const { return lo() == M_PI && hi() == -M_PI; }
+
+ // Return true if lo() > hi(). (This is true for empty intervals.)
+ bool is_inverted() const { return lo() > hi(); }
+
+ // Return the midpoint of the interval. For full and empty intervals,
+ // the result is arbitrary.
+ double GetCenter() const;
+
+ // Return the length of the interval. The length of an empty interval
+ // is negative.
+ double GetLength() const;
+
+ // Return the complement of the interior of the interval. An interval and
+ // its complement have the same boundary but do not share any interior
+ // values. The complement operator is not a bijection, since the complement
+ // of a singleton interval (containing a single value) is the same as the
+ // complement of an empty interval.
+ S1Interval Complement() const;
+
+ // Return the midpoint of the complement of the interval. For full and empty
+ // intervals, the result is arbitrary. For a singleton interval (containing a
+ // single point), the result is its antipodal point on S1.
+ double GetComplementCenter() const;
+
+ // Return true if the interval (which is closed) contains the point 'p'.
+ bool Contains(double p) const;
+
+ // Return true if the interior of the interval contains the point 'p'.
+ bool InteriorContains(double p) const;
+
+ // Return true if the interval contains the given interval 'y'.
+ // Works for empty, full, and singleton intervals.
+ bool Contains(const S1Interval& y) const;
+
+ // Returns true if the interior of this interval contains the entire
+ // interval 'y'. Note that x.InteriorContains(x) is true only when
+ // x is the empty or full interval, and x.InteriorContains(S1Interval(p,p))
+ // is equivalent to x.InteriorContains(p).
+ bool InteriorContains(const S1Interval& y) const;
+
+ // Return true if the two intervals contain any points in common.
+ // Note that the point +/-Pi has two representations, so the intervals
+ // [-Pi,-3] and [2,Pi] intersect, for example.
+ bool Intersects(const S1Interval& y) const;
+
+ // Return true if the interior of this interval contains any point of the
+ // interval 'y' (including its boundary). Works for empty, full, and
+ // singleton intervals.
+ bool InteriorIntersects(const S1Interval& y) const;
+
+ // Return the Hausdorff distance to the given interval 'y'. For two
+ // S1Intervals x and y, this distance is defined by
+ // h(x, y) = max_{p in x} min_{q in y} d(p, q),
+ // where d(.,.) is measured along S1.
+ double GetDirectedHausdorffDistance(const S1Interval& y) const;
+
+ // Expand the interval by the minimum amount necessary so that it
+ // contains the given point "p" (an angle in the range [-Pi, Pi]).
+ void AddPoint(double p);
+
+ // Return the closest point in the interval to the given point "p".
+ // The interval must be non-empty.
+ double Project(double p) const;
+
+ // Return an interval that has been expanded on each side by the given
+ // distance "margin". If "margin" is negative, then shrink the interval on
+ // each side by "margin" instead. The resulting interval may be empty or
+ // full. Any expansion (positive or negative) of a full interval remains
+ // full, and any expansion of an empty interval remains empty.
+ S1Interval Expanded(double margin) const;
+
+ // Return the smallest interval that contains this interval and the
+ // given interval "y".
+ S1Interval Union(const S1Interval& y) const;
+
+ // Return the smallest interval that contains the intersection of this
+ // interval with "y". Note that the region of intersection may
+ // consist of two disjoint intervals.
+ S1Interval Intersection(const S1Interval& y) const;
+
+ // Return true if two intervals contains the same set of points.
+ bool operator==(const S1Interval& y) const;
+
+ // Return true if this interval can be transformed into the given interval by
+ // moving each endpoint by at most "max_error" (and without the endpoints
+ // crossing, which would invert the interval). Empty and full intervals are
+ // considered to start at an arbitrary point on the unit circle, thus any
+ // interval with (length <= 2*max_error) matches the empty interval, and any
+ // interval with (length >= 2*Pi - 2*max_error) matches the full interval.
+ bool ApproxEquals(const S1Interval& y, double max_error = 1e-15) const;
+
+ // Low-level methods to modify one endpoint of an existing S1Interval.
+ // These methods should really be private because setting just one endpoint
+ // can violate the invariants maintained by S1Interval. In particular:
+ //
+ // - It is not valid to call these methods on an Empty() or Full()
+ // interval, since these intervals do not have any endpoints.
+ //
+ // - It is not allowed to set an endpoint to -Pi. (When these methods are
+ // used internally, values of -Pi have already been normalized to Pi.)
+ //
+ // The preferred way to modify both endpoints of an interval is to use a
+ // constructor, e.g. lng = S1Interval(lng_lo, lng_hi).
+ void set_lo(double p);
+ void set_hi(double p);
+
+ private:
+ enum ArgsChecked { ARGS_CHECKED };
+
+ // Internal constructor that assumes that both arguments are in the
+ // correct range, i.e. normalization from -Pi to Pi is already done.
+ S1Interval(double lo, double hi, ArgsChecked dummy);
+
+ // Return true if the interval (which is closed) contains the point 'p'.
+ // Skips the normalization of 'p' from -Pi to Pi.
+ bool FastContains(double p) const;
+
+ Vector2_d bounds_;
+};
+
+inline S1Interval::S1Interval(double lo, double hi) : bounds_(lo, hi) {
+ if (lo == -M_PI && hi != M_PI) set_lo(M_PI);
+ if (hi == -M_PI && lo != M_PI) set_hi(M_PI);
+ S2_DCHECK(is_valid());
+}
+
+inline S1Interval::S1Interval(double lo, double hi, ArgsChecked dummy)
+ : bounds_(lo, hi) {
+ S2_DCHECK(is_valid());
+}
+
+inline S1Interval::S1Interval() : bounds_(M_PI, -M_PI) {
+}
+
+inline S1Interval S1Interval::Empty() {
+ return S1Interval();
+}
+
+inline S1Interval S1Interval::Full() {
+ return S1Interval(-M_PI, M_PI, ARGS_CHECKED);
+}
+
+inline bool S1Interval::is_valid() const {
+ return (std::fabs(lo()) <= M_PI && std::fabs(hi()) <= M_PI &&
+ !(lo() == -M_PI && hi() != M_PI) &&
+ !(hi() == -M_PI && lo() != M_PI));
+}
+
+inline bool S1Interval::operator==(const S1Interval& y) const {
+ return lo() == y.lo() && hi() == y.hi();
+}
+
+inline void S1Interval::set_lo(double p) {
+ bounds_[0] = p;
+ S2_DCHECK(is_valid());
+}
+
+inline void S1Interval::set_hi(double p) {
+ bounds_[1] = p;
+ S2_DCHECK(is_valid());
+}
+
+inline std::ostream& operator<<(std::ostream& os, const S1Interval& x) {
+ return os << "[" << x.lo() << ", " << x.hi() << "]";
+}
+
+#endif // S2_S1INTERVAL_H_
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2BOOLEAN_OPERATION_H_
+#define S2_S2BOOLEAN_OPERATION_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+#include "s2/s2builder.h"
+#include "s2/s2builder_graph.h"
+#include "s2/s2builder_layer.h"
+#include "s2/value_lexicon.h"
+
+// This class implements boolean operations (intersection, union, difference,
+// and symmetric difference) for regions whose boundaries are defined by
+// geodesic edges.
+//
+// S2BooleanOperation operates on exactly two input regions at a time. Each
+// region is represented as an S2ShapeIndex and may contain any number of
+// points, polylines, and polygons. The region is essentially the union of
+// these objects, except that polygon interiors must be disjoint from all
+// other geometry (including other polygon interiors). If the input geometry
+// for a region does not meet this condition, it can be normalized by
+// computing its union first. Note that points or polylines are allowed to
+// coincide with the boundaries of polygons.
+//
+// Degeneracies are supported. A polygon loop or polyline may consist of a
+// single edge from a vertex to itself, and polygons may contain "sibling
+// pairs" consisting of an edge and its corresponding reverse edge. Polygons
+// must not have any duplicate edges (due to the requirement that polygon
+// interiors are disjoint), but polylines may have duplicate edges or can even
+// be self-intersecting.
+//
+// Points and polyline edges are treated as multisets: if the same point or
+// polyline edge appears multiple times in the input, it will appear multiple
+// times in the output. For example, the union of a point with an identical
+// point consists of two points. This feature is useful for modeling large
+// sets of points or polylines as a single region while maintaining their
+// distinct identities, even when the points or polylines intersect each
+// other. It is also useful for reconstructing polylines that loop back on
+// themselves. If duplicate geometry is not desired, it can be merged by
+// GraphOptions::DuplicateEdges::MERGE in the S2Builder output layer.
+//
+// Polylines are always considered to be directed. Polyline edges between the
+// same pair of vertices are defined to intersect even if the two edges are in
+// opposite directions. (Undirected polylines can be modeled by specifying
+// GraphOptions::EdgeType::UNDIRECTED in the S2Builder output layer.)
+//
+// The output of each operation is sent to an S2Builder::Layer provided by the
+// client. This allows clients to build any representation of the geometry
+// they choose. It also allows the client to do additional postprocessing of
+// the output before building data structures; for example, the client can
+// easily discard degeneracies or convert them to another data type.
+//
+// The boundaries of polygons and polylines can be modeled as open, semi-open,
+// or closed. Polyline boundaries are controlled by the PolylineModel class,
+// whose options are as follows:
+//
+// - In the OPEN model, polylines do not contain their first or last vertex
+// except for one special case: namely, if the polyline forms a loop and
+// the polyline_loops_have_boundaries() option is set to false, then the
+// first/last vertex is contained.
+//
+// - In the SEMI_OPEN model, polylines contain all vertices except the last.
+// Therefore if one polyline starts where another polyline stops, the two
+// polylines do not intersect.
+//
+// - In the CLOSED model, polylines contain all of their vertices.
+//
+// When multiple polylines are present, they are processed independently and
+// have no effect on each other. For example, in the OPEN boundary model the
+// polyline ABC contains the vertex B, while set of polylines {AB, BC} does
+// not. (If you want to treat the polylines as a union instead, with
+// boundaries merged according to the "mod 2" rule, this can be achieved by
+// reassembling the edges into maximal polylines using S2PolylineVectorLayer
+// with EdgeType::UNDIRECTED, DuplicateEdges::MERGE, and PolylineType::WALK.)
+//
+// Polygon boundaries are controlled by the PolygonModel class, which has the
+// following options:
+//
+// - In the OPEN model, polygons do not contain their vertices or edges.
+// This implies that a polyline that follows the boundary of a polygon will
+// not intersect it.
+//
+// - In the SEMI_OPEN model, polygon point containment is defined such that
+// if several polygons tile the region around a vertex, then exactly one of
+// those polygons contains that vertex. Similarly polygons contain all of
+// their edges, but none of their reversed edges. This implies that a
+// polyline and polygon edge with the same endpoints intersect if and only
+// if they are in the same direction. (This rule ensures that if a
+// polyline is intersected with a polygon and its complement, the two
+// resulting polylines do not have any edges in common.)
+//
+// - In the CLOSED model, polygons contain all their vertices, edges, and
+// reversed edges. This implies that a polyline that shares an edge (in
+// either direction) with a polygon is defined to intersect it. Similarly,
+// this is the only model where polygons that touch at a vertex or along an
+// edge intersect.
+//
+// Note that PolylineModel and PolygonModel are defined as separate classes in
+// order to allow for possible future extensions.
+//
+// Operations between geometry of different dimensions are defined as follows:
+//
+// - For UNION, the higher-dimensional shape always wins. For example the
+// union of a closed polygon A with a polyline B that coincides with the
+// boundary of A consists only of the polygon A.
+//
+// - For INTERSECTION, the lower-dimensional shape always wins. For example,
+// the intersection of a closed polygon A with a point B that coincides
+// with a vertex of A consists only of the point B.
+//
+// - For DIFFERENCE, higher-dimensional shapes are not affected by
+// subtracting lower-dimensional shapes. For example, subtracting a point
+// or polyline from a polygon A yields the original polygon A. This rule
+// exists because in general, it is impossible to represent the output
+// using the specified boundary model(s). (Consider subtracting one vertex
+// from a PolylineModel::CLOSED polyline, or subtracting one edge from a
+// PolygonModel::CLOSED polygon.) If you want to perform operations like
+// this, consider representing all boundaries explicitly (topological
+// boundaries) using OPEN boundary models. Another option for polygons is
+// to subtract a degenerate loop, which yields a polygon with a degenerate
+// hole (see S2LaxPolygonShape).
+//
+// Note that in the case of Precision::EXACT operations, the above remarks
+// only apply to the output before snapping. Snapping may cause nearby
+// distinct edges to become coincident, e.g. a polyline may become coincident
+// with a polygon boundary. However also note that S2BooleanOperation is
+// perfectly happy to accept such geometry as input.
+//
+// Note the following differences between S2BooleanOperation and the similar
+// S2MultiBooleanOperation class:
+//
+// - S2BooleanOperation operates on exactly two regions at a time, whereas
+// S2MultiBooleanOperation operates on any number of regions.
+//
+// - S2BooleanOperation is potentially much faster when the input is already
+// represented as S2ShapeIndexes. The algorithm is output sensitive and is
+// often sublinear in the input size. This can be a big advantage if, say,
+//
+// - S2BooleanOperation supports exact predicates and the corresponding
+// exact operations (i.e., operations that are equivalent to computing the
+// exact result and then snap rounding it).
+//
+// - S2MultiBooleanOperation has better error guarantees when there are many
+// regions, since it requires only one snapping operation for any number of
+// input regions.
+//
+// Example usage:
+// S2ShapeIndex a, b; // Input geometry, e.g. containing polygons.
+// S2Polygon polygon; // Output geometry.
+// S2BooleanOperation::Options options;
+// options.set_snap_function(snap_function);
+// S2BooleanOperation op(S2BooleanOperation::OpType::INTERSECTION,
+// absl::make_unique<S2PolygonLayer>(&polygon),
+// options);
+// S2Error error;
+// if (!op.Build(a, b, &error)) {
+// S2_LOG(ERROR) << error;
+// ...
+// }
+//
+// If the output includes objects of different dimensions, they can be
+// assembled into different layers with code like this:
+//
+// vector<S2Point> points;
+// vector<unique_ptr<S2Polyline>> polylines;
+// S2Polygon polygon;
+// S2BooleanOperation op(
+// S2BooleanOperation::OpType::UNION,
+// absl::make_unique<s2builderutil::PointVectorLayer>(&points),
+// absl::make_unique<s2builderutil::S2PolylineVectorLayer>(&polylines),
+// absl::make_unique<S2PolygonLayer>(&polygon));
+
+class S2BooleanOperation {
+ public:
+ // The supported operation types.
+ enum class OpType {
+ UNION, // Contained by either region.
+ INTERSECTION, // Contained by both regions.
+ DIFFERENCE, // Contained by the first region but not the second.
+ SYMMETRIC_DIFFERENCE // Contained by one region but not the other.
+ };
+ // Translates OpType to one of the strings above.
+ static const char* OpTypeToString(OpType op_type);
+
+ // Defines whether polygons are considered to contain their vertices and/or
+ // edges (see definitions above).
+ enum class PolygonModel { OPEN, SEMI_OPEN, CLOSED };
+
+ // Defines whether polylines are considered to contain their endpoints
+ // (see definitions above).
+ enum class PolylineModel { OPEN, SEMI_OPEN, CLOSED };
+
+ // With Precision::EXACT, the operation is evaluated using the exact input
+ // geometry. Predicates that use this option will produce exact results;
+ // for example, they can distinguish between a polyline that barely
+ // intersects a polygon from one that barely misses it. Constructive
+ // operations (ones that yield new geometry, as opposed to predicates) are
+ // implemented by computing the exact result and then snap rounding it
+ // according to the given snap_function() (see below). This is as close as
+ // it is possible to get to the exact result while requiring that vertex
+ // coordinates have type "double".
+ //
+ // With Precision::SNAPPED, the input regions are snapped together *before*
+ // the operation is evaluated. So for example, two polygons that overlap
+ // slightly will be treated as though they share a common boundary, and
+ // similarly two polygons that are slightly separated from each other will
+ // be treated as though they share a common boundary. Snapped results are
+ // useful for dealing with points, since in S2 the only points that lie
+ // exactly on a polyline or polygon edge are the endpoints of that edge.
+ //
+ // Conceptually, the difference between these two options is that with
+ // Precision::SNAPPED, the inputs are snap rounded (together), whereas with
+ // Precision::EXACT only the result is snap rounded.
+ enum class Precision { EXACT, SNAPPED };
+
+ // SourceId identifies an edge from one of the two input S2ShapeIndexes.
+ // It consists of a region id (0 or 1), a shape id within that region's
+ // S2ShapeIndex, and an edge id within that shape.
+ class SourceId {
+ public:
+ SourceId();
+ SourceId(int region_id, int32 shape_id, int32 edge_id);
+ explicit SourceId(int32 special_edge_id);
+ int region_id() const { return region_id_; }
+ int32 shape_id() const { return shape_id_; }
+ int32 edge_id() const { return edge_id_; }
+ // TODO(ericv): Convert to functions, define all 6 comparisons.
+ bool operator==(SourceId other) const;
+ bool operator<(SourceId other) const;
+
+ private:
+ uint32 region_id_ : 1;
+ uint32 shape_id_ : 31;
+ int32 edge_id_;
+ };
+
+ class Options {
+ public:
+ Options();
+
+ // Convenience constructor that calls set_snap_function().
+ explicit Options(const S2Builder::SnapFunction& snap_function);
+
+ // Specifies the function to be used for snap rounding.
+ //
+ // DEFAULT: s2builderutil::IdentitySnapFunction(S1Angle::Zero())
+ // - This does no snapping and preserves all input vertices exactly unless
+ // there are crossing edges, in which case the snap radius is increased
+ // to the maximum intersection point error (S2::kIntersectionError).
+ const S2Builder::SnapFunction& snap_function() const;
+ void set_snap_function(const S2Builder::SnapFunction& snap_function);
+
+ // Defines whether polygons are considered to contain their vertices
+ // and/or edges (see comments above).
+ //
+ // DEFAULT: PolygonModel::SEMI_OPEN
+ PolygonModel polygon_model() const;
+ void set_polygon_model(PolygonModel model);
+
+ // Defines whether polylines are considered to contain their vertices (see
+ // comments above).
+ //
+ // DEFAULT: PolylineModel::CLOSED
+ PolylineModel polyline_model() const;
+ void set_polyline_model(PolylineModel model);
+
+ // Specifies whether a polyline loop is considered to have a non-empty
+ // boundary. By default this option is true, meaning that even if the
+ // first and last vertices of a polyline are the same, the polyline is
+ // considered to have a well-defined "start" and "end". For example, if
+ // the polyline boundary model is OPEN then the polyline loop would not
+ // include the start/end vertices. These are the best semantics for most
+ // applications, such as GPS tracks or road network segments.
+ //
+ // If the polyline forms a loop and this option is set to false, then
+ // instead the first and last vertices are considered to represent a
+ // single vertex in the interior of the polyline. In this case the
+ // boundary of the polyline is empty, meaning that the first/last vertex
+ // will be contained by the polyline even if the boundary model is OPEN.
+ // (Note that this option also has a small effect on the CLOSED boundary
+ // model, because the first/last vertices of a polyline loop are
+ // considered to represent one vertex rather than two.)
+ //
+ // The main reason for this option is to implement the "mod 2 union"
+ // boundary semantics of the OpenGIS Simple Features spec. This can be
+ // achieved by making sure that all polylines are constructed using
+ // S2Builder::Graph::PolylineType::WALK (which ensures that all polylines
+ // are as long as possible), and then setting this option to false.
+ //
+ // DEFAULT: true
+ bool polyline_loops_have_boundaries() const;
+ void set_polyline_loops_have_boundaries(bool value);
+
+ // Specifies whether the operation should use the exact input geometry
+ // (Precision::EXACT), or whether the two input regions should be snapped
+ // together first (Precision::SNAPPED).
+ //
+ // DEFAULT: Precision::EXACT
+ Precision precision() const;
+ // void set_precision(Precision precision);
+
+ // If true, the input geometry is interpreted as representing nearby
+ // geometry that has been snapped or simplified. It then outputs a
+ // conservative result based on the value of polygon_model() and
+ // polyline_model(). For the most part, this only affects the handling of
+ // degeneracies.
+ //
+ // - If the model is OPEN, the result is as open as possible. For
+ // example, the intersection of two identical degenerate shells is empty
+ // under PolygonModel::OPEN because they could have been disjoint before
+ // snapping. Similarly, two identical degenerate polylines have an
+ // empty intersection under PolylineModel::OPEN.
+ //
+ // - If the model is CLOSED, the result is as closed as possible. In the
+ // case of the DIFFERENCE operation, this is equivalent to evaluating
+ // A - B as Closure(A) - Interior(B). For other operations, it affects
+ // only the handling of degeneracies. For example, the union of two
+ // identical degenerate holes is empty under PolygonModel::CLOSED
+ // (i.e., the hole disappears) because the holes could have been
+ // disjoint before snapping.
+ //
+ // - If the model is SEMI_OPEN, the result is as degenerate as possible.
+ // New degeneracies will not be created, but all degeneracies that
+ // coincide with the opposite region's boundary are retained unless this
+ // would cause a duplicate polygon edge to be created. This model is
+ // is very useful for working with input data that has both positive and
+ // negative degeneracies (i.e., degenerate shells and holes).
+ //
+ // DEFAULT: false
+ bool conservative_output() const;
+ // void set_conservative_output(bool conservative);
+
+ // If specified, then each output edge will be labelled with one or more
+ // SourceIds indicating which input edge(s) it corresponds to. This
+ // can be useful if your input geometry has additional data that needs to
+ // be propagated from the input to the output (e.g., elevations).
+ //
+ // You can access the labels by using an S2Builder::Layer type that
+ // supports labels, such as S2PolygonLayer. The layer outputs a
+ // "label_set_lexicon" and an "label_set_id" for each edge. You can then
+ // look up the source information for each edge like this:
+ //
+ // for (int32 label : label_set_lexicon.id_set(label_set_id)) {
+ // const SourceId& src = source_id_lexicon.value(label);
+ // // region_id() specifies which S2ShapeIndex the edge is from (0 or 1).
+ // DoSomething(src.region_id(), src.shape_id(), src.edge_id());
+ // }
+ //
+ // DEFAULT: nullptr
+ ValueLexicon<SourceId>* source_id_lexicon() const;
+ // void set_source_id_lexicon(ValueLexicon<SourceId>* source_id_lexicon);
+
+ // Options may be assigned and copied.
+ Options(const Options& options);
+ Options& operator=(const Options& options);
+
+ private:
+ std::unique_ptr<S2Builder::SnapFunction> snap_function_;
+ PolygonModel polygon_model_ = PolygonModel::SEMI_OPEN;
+ PolylineModel polyline_model_ = PolylineModel::CLOSED;
+ bool polyline_loops_have_boundaries_ = true;
+ Precision precision_ = Precision::EXACT;
+ bool conservative_output_ = false;
+ ValueLexicon<SourceId>* source_id_lexicon_ = nullptr;
+ };
+
+ S2BooleanOperation(OpType op_type,
+ std::unique_ptr<S2Builder::Layer> layer,
+ const Options& options = Options());
+
+ // Specifies that the output boundary edges should be sent to three
+ // different layers according to their dimension. Points (represented by
+ // degenerate edges) are sent to layer 0, polyline edges are sent to
+ // layer 1, and polygon edges are sent to layer 2.
+ //
+ // The dimension of an edge is defined as the minimum dimension of the two
+ // input edges that produced it. For example, the intersection of two
+ // crossing polyline edges is a considered to be a degenerate polyline
+ // rather than a point, so it is sent to layer 1. Clients can easily
+ // reclassify such polylines as points if desired, but this rule makes it
+ // easier for clients that want to process point, polyline, and polygon
+ // inputs differently.
+ //
+ // The layers are always built in the order 0, 1, 2, and all arguments to
+ // the Build() calls are guaranteed to be valid until the last call returns.
+ // All Graph objects have the same set of vertices and the same lexicon
+ // objects, in order to make it easier to write classes that process all the
+ // edges in parallel.
+ S2BooleanOperation(OpType op_type,
+ std::vector<std::unique_ptr<S2Builder::Layer>> layers,
+ const Options& options = Options());
+
+ OpType op_type() const { return op_type_; }
+
+ // Executes the given operation. Returns true on success, and otherwise
+ // sets "error" appropriately. (This class does not generate any errors
+ // itself, but the S2Builder::Layer might.)
+ bool Build(const S2ShapeIndex& a, const S2ShapeIndex& b,
+ S2Error* error);
+
+ // Convenience method that returns true if the result of the given operation
+ // is empty.
+ static bool IsEmpty(OpType op_type,
+ const S2ShapeIndex& a, const S2ShapeIndex& b,
+ const Options& options = Options());
+
+ // Convenience method that returns true if A intersects B.
+ static bool Intersects(const S2ShapeIndex& a, const S2ShapeIndex& b,
+ const Options& options = Options()) {
+ return !IsEmpty(OpType::INTERSECTION, b, a, options);
+ }
+
+ // Convenience method that returns true if A contains B, i.e., if the
+ // difference (B - A) is empty.
+ static bool Contains(const S2ShapeIndex& a, const S2ShapeIndex& b,
+ const Options& options = Options()) {
+ return IsEmpty(OpType::DIFFERENCE, b, a, options);
+ }
+
+ // Convenience method that returns true if the symmetric difference of A and
+ // B is empty. (Note that A and B may still not be identical, e.g. A may
+ // contain two copies of a polyline while B contains one.)
+ static bool Equals(const S2ShapeIndex& a, const S2ShapeIndex& b,
+ const Options& options = Options()) {
+ return IsEmpty(OpType::SYMMETRIC_DIFFERENCE, b, a, options);
+ }
+
+ private:
+ class Impl; // The actual implementation.
+
+ // Internal constructor to reduce code duplication.
+ S2BooleanOperation(OpType op_type, const Options& options);
+
+ // Specifies that "result_empty" should be set to indicate whether the exact
+ // result of the operation is empty. This constructor is used to efficiently
+ // test boolean relationships (see IsEmpty above).
+ S2BooleanOperation(OpType op_type, bool* result_empty,
+ const Options& options = Options());
+
+ OpType op_type_;
+ Options options_;
+
+ // The input regions.
+ const S2ShapeIndex* regions_[2];
+
+ // The output consists either of zero layers, one layer, or three layers.
+ std::vector<std::unique_ptr<S2Builder::Layer>> layers_;
+
+ // The following field is set if and only if there are no output layers.
+ bool* result_empty_;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline S2BooleanOperation::SourceId::SourceId()
+ : region_id_(0), shape_id_(0), edge_id_(-1) {
+}
+
+inline S2BooleanOperation::SourceId::SourceId(
+ int region_id, int32 shape_id, int32 edge_id)
+ : region_id_(region_id), shape_id_(shape_id), edge_id_(edge_id) {
+}
+
+inline S2BooleanOperation::SourceId::SourceId(int special_edge_id)
+ : region_id_(0), shape_id_(0), edge_id_(special_edge_id) {
+}
+
+inline bool S2BooleanOperation::SourceId::operator==(SourceId other) const {
+ return (region_id_ == other.region_id_ &&
+ shape_id_ == other.shape_id_ &&
+ edge_id_ == other.edge_id_);
+}
+
+inline bool S2BooleanOperation::SourceId::operator<(SourceId other) const {
+ if (region_id_ < other.region_id_) return true;
+ if (region_id_ > other.region_id_) return false;
+ if (shape_id_ < other.shape_id_) return true;
+ if (shape_id_ > other.shape_id_) return false;
+ return edge_id_ < other.edge_id_;
+}
+
+#endif // S2_S2BOOLEAN_OPERATION_H_
--- /dev/null
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// This class is a replacement for S2PolygonBuilder. Once all clients have
+// been updated to use this class, S2PolygonBuilder will be removed.
+
+#ifndef S2_S2BUILDER_H_
+#define S2_S2BUILDER_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+#include "s2/base/integral_types.h"
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/id_set_lexicon.h"
+#include "s2/mutable_s2shape_index.h"
+#include "s2/s1angle.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2error.h"
+#include "s2/s2point_index.h"
+#include "s2/s2shape_index.h"
+#include "s2/util/gtl/compact_array.h"
+
+class S2Loop;
+class S2Polygon;
+class S2Polyline;
+
+// S2Builder is a tool for assembling polygonal geometry from edges. Here are
+// some of the things it is designed for:
+//
+// 1. Building polygons, polylines, and polygon meshes from unsorted
+// collections of edges.
+//
+// 2. Snapping geometry to discrete representations (such as S2CellId centers
+// or E7 lat/lng coordinates) while preserving the input topology and with
+// guaranteed error bounds.
+//
+// 3. Simplifying geometry (e.g. for indexing, display, or storage).
+//
+// 4. Importing geometry from other formats, including repairing geometry
+// that has errors.
+//
+// 5. As a tool for implementing more complex operations such as polygon
+// intersections and unions.
+//
+// The implementation is based on the framework of "snap rounding". Unlike
+// most snap rounding implementations, S2Builder defines edges as geodesics on
+// the sphere (straight lines) and uses the topology of the sphere (i.e.,
+// there are no "seams" at the poles or 180th meridian). The algorithm is
+// designed to be 100% robust for arbitrary input geometry. It offers the
+// following properties:
+//
+// - Guaranteed bounds on how far input vertices and edges can move during
+// the snapping process (i.e., at most the given "snap_radius").
+//
+// - Guaranteed minimum separation between edges and vertices other than
+// their endpoints (similar to the goals of Iterated Snap Rounding). In
+// other words, edges that do not intersect in the output are guaranteed
+// to have a minimum separation between them.
+//
+// - Idempotency (similar to the goals of Stable Snap Rounding), i.e. if the
+// input already meets the output criteria then it will not be modified.
+//
+// - Preservation of the input topology (up to the creation of
+// degeneracies). This means that there exists a continuous deformation
+// from the input to the output such that no vertex crosses an edge. In
+// other words, self-intersections won't be created, loops won't change
+// orientation, etc.
+//
+// - The ability to snap to arbitrary discrete point sets (such as S2CellId
+// centers, E7 lat/lng points on the sphere, or simply a subset of the
+// input vertices), rather than being limited to an integer grid.
+//
+// Here are some of its other features:
+//
+// - It can handle both directed and undirected edges. Undirected edges can
+// be useful for importing data from other formats, e.g. where loops have
+// unspecified orientations.
+//
+// - It can eliminate self-intersections by finding all edge pairs that cross
+// and adding a new vertex at each intersection point.
+//
+// - It can simplify polygons to within a specified tolerance. For example,
+// if two vertices are close enough they will be merged, and if an edge
+// passes nearby a vertex then it will be rerouted through that vertex.
+// Optionally, it can also detect nearly straight chains of short edges and
+// replace them with a single long edge, while maintaining the same
+// accuracy, separation, and topology guarantees ("simplify_edge_chains").
+//
+// - It supports many different output types through the concept of "layers"
+// (polylines, polygons, polygon meshes, etc). You can build multiple
+// layers at once in order to ensure that snapping does not create
+// intersections between different objects (for example, you can simplify a
+// set of contour lines without the risk of having them cross each other).
+//
+// - It supports edge labels, which allow you to attach arbitrary information
+// to edges and have it preserved during the snapping process. (This can
+// also be achieved using layers, at a coarser level of granularity.)
+//
+// Caveats:
+//
+// - Because S2Builder only works with edges, it cannot distinguish between
+// the empty and full polygons. If your application can generate both the
+// empty and full polygons, you must implement logic outside of this class.
+//
+// Example showing how to snap a polygon to E7 coordinates:
+//
+// using s2builderutil::IntLatLngSnapFunction;
+// S2Builder builder(S2Builder::Options(IntLatLngSnapFunction(7)));
+// S2Polygon output;
+// builder.StartLayer(absl::make_unique<s2builderutil::S2PolygonLayer>(&output));
+// builder.AddPolygon(input);
+// S2Error error;
+// if (!builder.Build(&error)) {
+// S2_LOG(ERROR) << error;
+// ...
+// }
+class S2Builder {
+ public:
+ // Indicates whether the input edges are undirected. Typically this is
+ // specified for each output layer (e.g., s2builderutil::S2PolygonLayer).
+ //
+ // Directed edges are preferred, since otherwise the output is ambiguous.
+ // For example, output polygons may be the *inverse* of the intended result
+ // (e.g., a polygon intended to represent the world's oceans may instead
+ // represent the world's land masses). Directed edges are also somewhat
+ // more efficient.
+ //
+ // However even with undirected edges, most S2Builder layer types try to
+ // preserve the input edge direction whenever possible. Generally, edges
+ // are reversed only when it would yield a simpler output. For example,
+ // S2PolygonLayer assumes that polygons created from undirected edges should
+ // cover at most half of the sphere. Similarly, S2PolylineVectorLayer
+ // assembles edges into as few polylines as possible, even if this means
+ // reversing some of the "undirected" input edges.
+ //
+ // For shapes with interiors, directed edges should be oriented so that the
+ // interior is to the left of all edges. This means that for a polygon with
+ // holes, the outer loops ("shells") should be directed counter-clockwise
+ // while the inner loops ("holes") should be directed clockwise. Note that
+ // S2Builder::AddPolygon() follows this convention automatically.
+ enum class EdgeType { DIRECTED, UNDIRECTED };
+
+ // A SnapFunction restricts the locations of the output vertices. For
+ // example, there are predefined snap functions that require vertices to be
+ // located at S2CellId centers or at E5/E6/E7 coordinates. The SnapFunction
+ // can also specify a minimum spacing between vertices (the "snap radius").
+ //
+ // A SnapFunction defines the following methods:
+ //
+ // 1. The SnapPoint() method, which snaps a point P to a nearby point (the
+ // "candidate snap site"). Any point may be returned, including P
+ // itself (this is the "identity snap function").
+ //
+ // 2. "snap_radius", the maximum distance that vertices can move when
+ // snapped. The snap_radius must be at least as large as the maximum
+ // distance between P and SnapPoint(P) for any point P.
+ //
+ // 3. "max_edge_deviation", the maximum distance that edges can move when
+ // snapped. It is slightly larger than "snap_radius" because when a
+ // geodesic edge is snapped, the center of the edge moves further than
+ // its endpoints. This value is computed automatically by S2Builder.
+ //
+ // 4. "min_vertex_separation", the guaranteed minimum distance between
+ // vertices in the output. This is generally a fraction of
+ // "snap_radius" where the fraction depends on the snap function.
+ //
+ // 5. A "min_edge_vertex_separation", the guaranteed minimum distance
+ // between edges and non-incident vertices in the output. This is
+ // generally a fraction of "snap_radius" where the fraction depends on
+ // the snap function.
+ //
+ // It is important to note that SnapPoint() does not define the actual
+ // mapping from input vertices to output vertices, since the points it
+ // returns (the candidate snap sites) are further filtered to ensure that
+ // they are separated by at least the snap radius. For example, if you
+ // specify E7 coordinates (2cm resolution) and a snap radius of 10m, then a
+ // subset of points returned by SnapPoint will be chosen (the "snap sites"),
+ // and each input vertex will be mapped to the closest site. Therefore you
+ // cannot assume that P is necessarily snapped to SnapPoint(P).
+ //
+ // S2Builder makes the following guarantees:
+ //
+ // 1. Every vertex is at a location returned by SnapPoint().
+ //
+ // 2. Vertices are within "snap_radius" of the corresponding input vertex.
+ //
+ // 3. Edges are within "max_edge_deviation" of the corresponding input edge
+ // (a distance slightly larger than "snap_radius").
+ //
+ // 4. Vertices are separated by at least "min_vertex_separation"
+ // (a fraction of "snap_radius" that depends on the snap function).
+ //
+ // 5. Edges and non-incident vertices are separated by at least
+ // "min_edge_vertex_separation" (a fraction of "snap_radius").
+ //
+ // 6. Vertex and edge locations do not change unless one of the conditions
+ // above is not already met (idempotency / stability).
+ //
+ // 7. The topology of the input geometry is preserved (up to the creation
+ // of degeneracies). This means that there exists a continuous
+ // deformation from the input to the output such that no vertex
+ // crosses an edge.
+ class SnapFunction {
+ public:
+ virtual ~SnapFunction() {}
+
+ // The maximum distance that vertices can move when snapped.
+ //
+ // If the snap radius is zero, then vertices are snapped together only if
+ // they are identical. Edges will not be snapped to any vertices other
+ // than their endpoints, even if there are vertices whose distance to the
+ // edge is zero, unless split_crossing_edges() is true.
+ //
+ // REQUIRES: snap_radius() <= kMaxSnapRadius
+ virtual S1Angle snap_radius() const = 0;
+
+ // The maximum supported snap radius (equivalent to about 7800km).
+ static S1Angle kMaxSnapRadius();
+
+ // The maximum distance that the center of an edge can move when snapped.
+ // This is slightly larger than "snap_radius" because when a geodesic edge
+ // is snapped, the center of the edge moves further than its endpoints.
+ S1Angle max_edge_deviation() const;
+
+ // The guaranteed minimum distance between vertices in the output.
+ // This is generally some fraction of "snap_radius".
+ virtual S1Angle min_vertex_separation() const = 0;
+
+ // The guaranteed minimum spacing between edges and non-incident vertices
+ // in the output. This is generally some fraction of "snap_radius".
+ virtual S1Angle min_edge_vertex_separation() const = 0;
+
+ // Returns a candidate snap site for the given point. The final vertex
+ // locations are a subset of the snap sites returned by this function
+ // (spaced at least "min_vertex_separation" apart).
+ //
+ // The only requirement is that SnapPoint(x) must return a point whose
+ // distance from "x" is no greater than "snap_radius".
+ virtual S2Point SnapPoint(const S2Point& point) const = 0;
+
+ // Returns a deep copy of this SnapFunction.
+ virtual std::unique_ptr<SnapFunction> Clone() const = 0;
+ };
+
+ class Options {
+ public:
+ Options();
+
+ // Convenience constructor that calls set_snap_function().
+ explicit Options(const SnapFunction& snap_function);
+
+ // Sets the desired snap function. The snap function is copied
+ // internally, so you can safely pass a temporary object.
+ //
+ // Note that if your input data includes vertices that were created using
+ // S2::GetIntersection(), then you should use a "snap_radius" of
+ // at least S2::kIntersectionSnapRadius, e.g. by calling
+ //
+ // options.set_snap_function(s2builderutil::IdentitySnapFunction(
+ // S2::kIntersectionSnapRadius));
+ //
+ // DEFAULT: s2builderutil::IdentitySnapFunction(S1Angle::Zero())
+ // [This does no snapping and preserves all input vertices exactly.]
+ const SnapFunction& snap_function() const;
+ void set_snap_function(const SnapFunction& snap_function);
+
+ // If true, then detect all pairs of crossing edges and eliminate them by
+ // adding a new vertex at their intersection point.
+ //
+ // When this option is true, the effective snap_radius() for edges is
+ // increased by S2::kIntersectionError to take into account the
+ // additional error when computing intersection points. In other words,
+ // edges may move by up to snap_radius() + S2::kIntersectionError.
+ //
+ // Undirected edges should always be used when the output is a polygon,
+ // since splitting a directed loop at a self-intersection converts it into
+ // two loops that don't define a consistent interior according to the
+ // "interior is on the left" rule. (On the other hand, it is fine to use
+ // directed edges when defining a polygon *mesh* because in that case the
+ // input consists of sibling edge pairs.)
+ //
+ // Self-intersections can also arise when importing data from a 2D
+ // projection. You can minimize this problem by subdividing the input
+ // edges so that the S2 edges (which are geodesics) stay close to the
+ // original projected edges (which are curves on the sphere). This can
+ // be done using s2builderutil::EdgeSplitter(), for example.
+ //
+ // DEFAULT: false
+ bool split_crossing_edges() const;
+ void set_split_crossing_edges(bool split_crossing_edges);
+
+ // If true, then simplify the output geometry by replacing nearly straight
+ // chains of short edges with a single long edge.
+ //
+ // The combined effect of snapping and simplifying will not change the
+ // input by more than the guaranteed tolerances (see the list documented
+ // with the SnapFunction class). For example, simplified edges are
+ // guaranteed to pass within snap_radius() of the *original* positions of
+ // all vertices that were removed from that edge. This is a much tighter
+ // guarantee than can be achieved by snapping and simplifying separately.
+ //
+ // However, note that this option does not guarantee idempotency. In
+ // other words, simplifying geometry that has already been simplified once
+ // may simplify it further. (This is unavoidable, since tolerances are
+ // measured with respect to the original geometry, which is no longer
+ // available when the geometry is simplified a second time.)
+ //
+ // When the output consists of multiple layers, simplification is
+ // guaranteed to be consistent: for example, edge chains are simplified in
+ // the same way across layers, and simplification preserves topological
+ // relationships between layers (e.g., no crossing edges will be created).
+ // Note that edge chains in different layers do not need to be identical
+ // (or even have the same number of vertices, etc) in order to be
+ // simplified together. All that is required is that they are close
+ // enough together so that the same simplified edge can meet all of their
+ // individual snapping guarantees.
+ //
+ // Note that edge chains are approximated as parametric curves rather than
+ // point sets. This means that if an edge chain backtracks on itself (for
+ // example, ABCDEFEDCDEFGH) then such backtracking will be preserved to
+ // within snap_radius() (for example, if the preceding point were all in a
+ // straight line then the edge chain would be simplified to ACFCFH, noting
+ // that C and F have degree > 2 and therefore can't be simplified away).
+ //
+ // Simplified edges are assigned all labels associated with the edges of
+ // the simplified chain.
+ //
+ // For this option to have any effect, a SnapFunction with a non-zero
+ // snap_radius() must be specified. Also note that vertices specified
+ // using ForceVertex are never simplified away.
+ //
+ // DEFAULT: false
+ bool simplify_edge_chains() const;
+ void set_simplify_edge_chains(bool simplify_edge_chains);
+
+ // If true, then snapping occurs only when the input geometry does not
+ // already meet the S2Builder output guarantees (see the SnapFunction
+ // class description for details). This means that if all input vertices
+ // are at snapped locations, all vertex pairs are separated by at least
+ // min_vertex_separation(), and all edge-vertex pairs are separated by at
+ // least min_edge_vertex_separation(), then no snapping is done.
+ //
+ // If false, then all vertex pairs and edge-vertex pairs closer than
+ // "snap_radius" will be considered for snapping. This can be useful, for
+ // example, if you know that your geometry contains errors and you want to
+ // make sure that features closer together than "snap_radius" are merged.
+ //
+ // This option is automatically turned off by simplify_edge_chains(),
+ // since simplifying edge chains is never guaranteed to be idempotent.
+ //
+ // DEFAULT: true
+ bool idempotent() const;
+ void set_idempotent(bool idempotent);
+
+ // Options may be assigned and copied.
+ Options(const Options& options);
+ Options& operator=(const Options& options);
+
+ private:
+ std::unique_ptr<SnapFunction> snap_function_;
+ bool split_crossing_edges_ = false;
+ bool simplify_edge_chains_ = false;
+ bool idempotent_ = true;
+ };
+
+ // The following classes are only needed by Layer implementations.
+ class GraphOptions;
+ class Graph;
+
+ // For output layers that represent polygons, there is an ambiguity inherent
+ // in spherical geometry that does not exist in planar geometry. Namely, if
+ // a polygon has no edges, does it represent the empty polygon (containing
+ // no points) or the full polygon (containing all points)? This ambiguity
+ // also occurs for polygons that consist only of degeneracies, e.g. a
+ // degenerate loop with only two edges could be either a degenerate shell in
+ // the empty polygon or a degenerate hole in the full polygon.
+ //
+ // To resolve this ambiguity, an IsFullPolygonPredicate may be specified for
+ // each output layer (see AddIsFullPolygonPredicate below). If the output
+ // after snapping consists only of degenerate edges and/or sibling pairs
+ // (including the case where there are no edges at all), then the layer
+ // implementation calls the given predicate to determine whether the polygon
+ // is empty or full except for those degeneracies. The predicate is given
+ // an S2Builder::Graph containing the output edges, but note that in general
+ // the predicate must also have knowledge of the input geometry in order to
+ // determine the correct result.
+ //
+ // This predicate is only needed by layers that are assembled into polygons.
+ // It is not used by other layer types.
+ using IsFullPolygonPredicate =
+ std::function<bool (const Graph& g, S2Error* error)>;
+
+ // Default constructor; requires Init() to be called.
+ S2Builder();
+
+ // Convenience constructor that calls Init(). Note that to use the default
+ // options, C++ syntax requires an extra layer of parentheses:
+ //
+ // S2Builder builder{S2Builder::Options()};
+ explicit S2Builder(const Options& options);
+
+ // Initializes an S2Builder with the given options.
+ void Init(const Options& options);
+ const Options& options() const { return options_; }
+
+ // Starts a new output layer. This method must be called before adding any
+ // edges to the S2Builder. You may call this method multiple times to build
+ // multiple geometric objects that are snapped to the same set of sites.
+ //
+ // For example, if you have a set of contour lines, then you could put each
+ // contour line in a separate layer. This keeps the contour lines separate
+ // from each other, while also ensuring that no crossing edges are created
+ // when they are snapped and/or simplified. (This is not true if the
+ // contour lines are snapped or simplified independently.)
+ //
+ // Similarly, if you have a set of polygons that share common boundaries
+ // (e.g., countries), you can snap and/or simplify them at the same time by
+ // putting them in different layers, while ensuring that their boundaries
+ // remain consistent (i.e., no crossing edges or T-vertices are introduced).
+ //
+ // Ownership of the layer is transferred to the S2Builder. Example usage:
+ //
+ // S2Polyline line1, line2;
+ // builder.StartLayer(make_unique<s2builderutil::S2PolylineLayer>(&line1)));
+ // ... Add edges using builder.AddEdge(), etc ...
+ // builder.StartLayer(make_unique<s2builderutil::S2PolylineLayer>(&line2)));
+ // ... Add edges using builder.AddEdge(), etc ...
+ // S2Error error;
+ // S2_CHECK(builder.Build(&error)) << error; // Builds "line1" & "line2"
+ class Layer;
+ void StartLayer(std::unique_ptr<Layer> layer);
+
+ // Adds a degenerate edge (representing a point) to the current layer.
+ void AddPoint(const S2Point& v);
+
+ // Adds the given edge to the current layer.
+ void AddEdge(const S2Point& v0, const S2Point& v1);
+
+ // Adds the edges in the given polyline. (Note that if the polyline
+ // consists of 0 or 1 vertices, this method does nothing.)
+ void AddPolyline(const S2Polyline& polyline);
+
+ // Adds the edges in the given loop. If the sign() of the loop is negative
+ // (i.e. this loop represents a hole within a polygon), the edge directions
+ // are automatically reversed to ensure that the polygon interior is always
+ // to the left of every edge.
+ void AddLoop(const S2Loop& loop);
+
+ // Adds the loops in the given polygon. Loops representing holes have their
+ // edge directions automatically reversed as described for AddLoop(). Note
+ // that this method does not distinguish between the empty and full polygons,
+ // i.e. adding a full polygon has the same effect as adding an empty one.
+ void AddPolygon(const S2Polygon& polygon);
+
+ // Adds the edges of the given shape to the current layer.
+ void AddShape(const S2Shape& shape);
+
+ // For layers that are assembled into polygons, this method specifies a
+ // predicate that is called when the output consists entirely of degenerate
+ // edges and/or sibling pairs. The predicate is given an S2Builder::Graph
+ // containing the output edges (if any) and is responsible for deciding
+ // whether this graph represents the empty polygon (possibly with degenerate
+ // shells) or the full polygon (possibly with degenerate holes). Note that
+ // this cannot be determined from the output edges alone; it also requires
+ // knowledge of the input geometry. (Also see IsFullPolygonPredicate above.)
+ //
+ // This method should be called at most once per layer; additional calls
+ // simply overwrite the previous value for the current layer.
+ //
+ // The default predicate simply returns false (i.e., degenerate polygons are
+ // assumed to be empty). Arguably it would better to return an error in
+ // this case, but the fact is that relatively few clients need to be able to
+ // construct full polygons, and it is unreasonable to expect all such
+ // clients to supply an appropriate predicate.
+ //
+ // The reason for having a predicate rather than a boolean value is that the
+ // predicate is responsible for determining whether the output polygon is
+ // empty or full. In general the input geometry is not degenerate, but
+ // rather collapses into a degenerate configuration due to snapping and/or
+ // simplification.
+ //
+ // TODO(ericv): Provide standard predicates to handle common cases,
+ // e.g. valid input geometry that becomes degenerate due to snapping.
+ void AddIsFullPolygonPredicate(IsFullPolygonPredicate predicate);
+
+ // A predicate that returns an error indicating that no polygon predicate
+ // has been specified.
+ static bool IsFullPolygonUnspecified(const S2Builder::Graph& g,
+ S2Error* error);
+
+ // Returns a predicate that returns a constant value (true or false);
+ static IsFullPolygonPredicate IsFullPolygon(bool is_full);
+
+ // Forces a vertex to be located at the given position. This can be used to
+ // prevent certain input vertices from moving. However if you are trying to
+ // preserve part of the input boundary, be aware that this option does not
+ // prevent edges from being split by new vertices.
+ //
+ // Forced vertices are never snapped; if this is desired then you need to
+ // call options().snap_function().SnapPoint() explicitly. Forced vertices
+ // are also never simplified away (if simplify_edge_chains() is used).
+ //
+ // Caveat: Since this method can place vertices arbitrarily close together,
+ // S2Builder makes no minimum separation guaranteees with forced vertices.
+ void ForceVertex(const S2Point& vertex);
+
+ // Every edge can have a set of non-negative integer labels attached to it.
+ // When used with an appropriate layer type, you can then retrieve the
+ // labels associated with each output edge. This can be useful when merging
+ // or combining data from several sources. (Note that in many cases it is
+ // easier to use separate output layers rather than labels.)
+ //
+ // Labels are 32-bit non-negative integers. To support other label types,
+ // you can use ValueLexicon to store the set of unique labels seen so far:
+ //
+ // ValueLexicon<MyLabel> my_label_lexicon;
+ // builder.set_label(my_label_lexicon.Add(label));
+ //
+ // The current set of labels is represented as a stack. This makes it easy
+ // to add and remove labels hierarchically (e.g., polygon 5, loop 2). Use
+ // set_label() and clear_labels() if you need at most one label per edge.
+ //
+ using Label = int32;
+
+ // Clear the stack of labels.
+ void clear_labels();
+
+ // Add a label to the stack.
+ // REQUIRES: label >= 0.
+ void push_label(Label label);
+
+ // Remove a label from the stack.
+ void pop_label();
+
+ // Convenience function that clears the stack and adds a single label.
+ // REQUIRES: label >= 0.
+ void set_label(Label label);
+
+ // Performs the requested edge splitting, snapping, simplification, etc, and
+ // then assembles the resulting edges into the requested output layers.
+ //
+ // Returns true if all edges were assembled; otherwise sets "error"
+ // appropriately. Depending on the error, some or all output layers may
+ // have been created. Automatically resets the S2Builder state so that it
+ // can be reused.
+ //
+ // REQUIRES: error != nullptr.
+ bool Build(S2Error* error);
+
+ // Clears all input data and resets the builder state. Any options
+ // specified are preserved.
+ void Reset();
+
+ private:
+ ////////////////////// Input Types /////////////////////////
+ // All types associated with the S2Builder inputs are prefixed with "Input".
+
+ // Identifies an input vertex.
+ using InputVertexId = int32;
+
+ // Defines an input edge.
+ using InputEdge = std::pair<InputVertexId, InputVertexId>;
+
+ // Identifies an input edge.
+ using InputEdgeId = int32;
+
+ // Identifies the set of input edge ids that were snapped to a given edge.
+ using InputEdgeIdSetId = int32;
+
+ // Sort key for prioritizing input vertices. (Note that keys are *not*
+ // compared using std::less; see SortInputVertices for details.)
+ using InputVertexKey = std::pair<S2CellId, InputVertexId>;
+
+ ////////////////////// Output Types /////////////////////////
+ // These types define the output vertices and edges.
+
+ // Identifies a snapped vertex ("snap site"). If there is only one layer,
+ // than SiteId is the same as Graph::VertexId, but if there are many layers
+ // then each Graph may contain only a subset of the sites. Also see
+ // GraphOptions::allow_vertex_filtering().
+ using SiteId = int32;
+
+ // Defines an output edge.
+ using Edge = std::pair<SiteId, SiteId>;
+
+ // Identifies an output edge.
+ using EdgeId = int32;
+
+ // Identifies an output edge in a particular layer.
+ using LayerEdgeId = std::pair<int, EdgeId>;
+
+ class EdgeChainSimplifier;
+
+ InputVertexId AddVertex(const S2Point& v);
+ void ChooseSites();
+ void CopyInputEdges();
+ std::vector<InputVertexKey> SortInputVertices();
+ void AddEdgeCrossings(const MutableS2ShapeIndex& input_edge_index);
+ void AddForcedSites(S2PointIndex<SiteId>* site_index);
+ bool is_forced(SiteId v) const;
+ void ChooseInitialSites(S2PointIndex<SiteId>* site_index);
+ S2Point SnapSite(const S2Point& point) const;
+ void CollectSiteEdges(const S2PointIndex<SiteId>& site_index);
+ void SortSitesByDistance(const S2Point& x,
+ gtl::compact_array<SiteId>* sites) const;
+ void AddExtraSites(const MutableS2ShapeIndex& input_edge_index);
+ void MaybeAddExtraSites(InputEdgeId edge_id,
+ InputEdgeId max_edge_id,
+ const std::vector<SiteId>& chain,
+ const MutableS2ShapeIndex& input_edge_index,
+ std::vector<InputEdgeId>* snap_queue);
+ void AddExtraSite(const S2Point& new_site,
+ InputEdgeId max_edge_id,
+ const MutableS2ShapeIndex& input_edge_index,
+ std::vector<InputEdgeId>* snap_queue);
+ S2Point GetSeparationSite(const S2Point& site_to_avoid,
+ const S2Point& v0, const S2Point& v1,
+ InputEdgeId input_edge_id) const;
+ S2Point GetCoverageEndpoint(const S2Point& p, const S2Point& x,
+ const S2Point& y, const S2Point& n) const;
+ void SnapEdge(InputEdgeId e, std::vector<SiteId>* chain) const;
+
+ void BuildLayers();
+ void BuildLayerEdges(
+ std::vector<std::vector<Edge>>* layer_edges,
+ std::vector<std::vector<InputEdgeIdSetId>>* layer_input_edge_ids,
+ IdSetLexicon* input_edge_id_set_lexicon);
+ void AddSnappedEdges(
+ InputEdgeId begin, InputEdgeId end, const GraphOptions& options,
+ std::vector<Edge>* edges, std::vector<InputEdgeIdSetId>* input_edge_ids,
+ IdSetLexicon* input_edge_id_set_lexicon,
+ std::vector<gtl::compact_array<InputVertexId>>* site_vertices) const;
+ void MaybeAddInputVertex(
+ InputVertexId v, SiteId id,
+ std::vector<gtl::compact_array<InputVertexId>>* site_vertices) const;
+ void AddSnappedEdge(SiteId src, SiteId dst, InputEdgeIdSetId id,
+ EdgeType edge_type, std::vector<Edge>* edges,
+ std::vector<InputEdgeIdSetId>* input_edge_ids) const;
+ void SimplifyEdgeChains(
+ const std::vector<gtl::compact_array<InputVertexId>>& site_vertices,
+ std::vector<std::vector<Edge>>* layer_edges,
+ std::vector<std::vector<InputEdgeIdSetId>>* layer_input_edge_ids,
+ IdSetLexicon* input_edge_id_set_lexicon) const;
+ void MergeLayerEdges(
+ const std::vector<std::vector<Edge>>& layer_edges,
+ const std::vector<std::vector<InputEdgeIdSetId>>& layer_input_edge_ids,
+ std::vector<Edge>* edges,
+ std::vector<InputEdgeIdSetId>* input_edge_ids,
+ std::vector<int>* edge_layers) const;
+ static bool StableLessThan(const Edge& a, const Edge& b,
+ const LayerEdgeId& ai, const LayerEdgeId& bi);
+
+ //////////// Parameters /////////////
+
+ // S2Builder options.
+ Options options_;
+
+ // The maximum distance (inclusive) that a vertex can move when snapped,
+ // equal to S1ChordAngle(options_.snap_function().snap_radius()).
+ S1ChordAngle site_snap_radius_ca_;
+
+ // The maximum distance (inclusive) that an edge can move when snapping to a
+ // snap site. It can be slightly larger than the site snap radius when
+ // edges are being split at crossings.
+ S1ChordAngle edge_snap_radius_ca_;
+
+ S1Angle max_edge_deviation_;
+ S1ChordAngle edge_site_query_radius_ca_;
+ S1ChordAngle min_edge_length_to_split_ca_;
+
+ S1Angle min_site_separation_;
+ S1ChordAngle min_site_separation_ca_;
+ S1ChordAngle min_edge_site_separation_ca_;
+ S1ChordAngle min_edge_site_separation_ca_limit_;
+
+ S1ChordAngle max_adjacent_site_separation_ca_;
+
+ // The squared sine of the edge snap radius. This is equivalent to the snap
+ // radius (squared) for distances measured through the interior of the
+ // sphere to the plane containing an edge. This value is used only when
+ // interpolating new points along edges (see GetSeparationSite).
+ double edge_snap_radius_sin2_;
+
+ // A copy of the argument to Build().
+ S2Error* error_;
+
+ // True if snapping was requested. This is true if either snap_radius() is
+ // positive, or split_crossing_edges() is true (which implicitly requests
+ // snapping to ensure that both crossing edges are snapped to the
+ // intersection point).
+ bool snapping_requested_;
+
+ // Initially false, and set to true when it is discovered that at least one
+ // input vertex or edge does not meet the output guarantees (e.g., that
+ // vertices are separated by at least snap_function.min_vertex_separation).
+ bool snapping_needed_;
+
+ //////////// Input Data /////////////
+
+ // A flag indicating whether label_set_ has been modified since the last
+ // time label_set_id_ was computed.
+ bool label_set_modified_;
+
+ std::vector<S2Point> input_vertices_;
+ std::vector<InputEdge> input_edges_;
+
+ std::vector<std::unique_ptr<Layer>> layers_;
+ std::vector<GraphOptions> layer_options_;
+ std::vector<InputEdgeId> layer_begins_;
+ std::vector<IsFullPolygonPredicate> layer_is_full_polygon_predicates_;
+
+ // Each input edge has "label set id" (an int32) representing the set of
+ // labels attached to that edge. This vector is populated only if at least
+ // one label is used.
+ using LabelSetId = int32;
+ std::vector<LabelSetId> label_set_ids_;
+ IdSetLexicon label_set_lexicon_;
+
+ // The current set of labels (represented as a stack).
+ std::vector<Label> label_set_;
+
+ // The LabelSetId corresponding to the current label set, computed on demand
+ // (by adding it to label_set_lexicon()).
+ LabelSetId label_set_id_;
+
+ ////////////// Data for Snapping and Simplifying //////////////
+
+ // The number of sites specified using ForceVertex(). These sites are
+ // always at the beginning of the sites_ vector.
+ SiteId num_forced_sites_;
+
+ // The set of snapped vertex locations ("sites").
+ std::vector<S2Point> sites_;
+
+ // A map from each input edge to the set of sites "nearby" that edge,
+ // defined as the set of sites that are candidates for snapping and/or
+ // avoidance. Note that compact_array will inline up to two sites, which
+ // usually takes care of the vast majority of edges. Sites are kept sorted
+ // by increasing distance from the origin of the input edge.
+ //
+ // Once snapping is finished, this field is discarded unless edge chain
+ // simplification was requested, in which case instead the sites are
+ // filtered by removing the ones that each edge was snapped to, leaving only
+ // the "sites to avoid" (needed for simplification).
+ std::vector<gtl::compact_array<SiteId>> edge_sites_;
+
+ S2Builder(const S2Builder&) = delete;
+ S2Builder& operator=(const S2Builder&) = delete;
+};
+
+// This class is only needed by S2Builder::Layer implementations. A layer is
+// responsible for assembling an S2Builder::Graph of snapped edges into the
+// desired output format (e.g., an S2Polygon). The GraphOptions class allows
+// each Layer type to specify requirements on its input graph: for example, if
+// DegenerateEdges::DISCARD is specified, then S2Builder will ensure that all
+// degenerate edges are removed before passing the graph to the S2Layer::Build
+// method.
+class S2Builder::GraphOptions {
+ public:
+ using EdgeType = S2Builder::EdgeType;
+ enum class DegenerateEdges;
+ enum class DuplicateEdges;
+ enum class SiblingPairs;
+
+ // All S2Builder::Layer subtypes should specify GraphOptions explicitly
+ // using this constructor, rather than relying on default values.
+ GraphOptions(EdgeType edge_type, DegenerateEdges degenerate_edges,
+ DuplicateEdges duplicate_edges, SiblingPairs sibling_pairs)
+ : edge_type_(edge_type), degenerate_edges_(degenerate_edges),
+ duplicate_edges_(duplicate_edges), sibling_pairs_(sibling_pairs),
+ allow_vertex_filtering_(true) {
+ }
+
+ // The default options specify that all edges should be kept, since this
+ // produces the least surprising output and makes it easier to diagnose the
+ // problem when an option is left unspecified.
+ GraphOptions() : edge_type_(EdgeType::DIRECTED),
+ degenerate_edges_(DegenerateEdges::KEEP),
+ duplicate_edges_(DuplicateEdges::KEEP),
+ sibling_pairs_(SiblingPairs::KEEP),
+ allow_vertex_filtering_(true) {
+ }
+
+ // Specifies whether the S2Builder input edges should be treated as
+ // undirected. If true, then all input edges are duplicated into pairs
+ // consisting of an edge and a sibling (reverse) edge. The layer
+ // implementation is responsible for ensuring that exactly one edge from
+ // each pair is used in the output, i.e. *only half* of the graph edges will
+ // be used. (Note that some values of the sibling_pairs() option
+ // automatically take care of this issue by removing half of the edges and
+ // changing edge_type() to DIRECTED.)
+ //
+ // DEFAULT: EdgeType::DIRECTED
+ EdgeType edge_type() const;
+ void set_edge_type(EdgeType edge_type);
+
+ // Controls how degenerate edges (i.e., an edge from a vertex to itself) are
+ // handled. Such edges may be present in the input, or they may be created
+ // when both endpoints of an edge are snapped to the same output vertex.
+ // The options available are:
+ //
+ // DISCARD: Discards all degenerate edges. This is useful for layers that
+ // do not support degeneracies, such as S2PolygonLayer.
+ //
+ // DISCARD_EXCESS: Discards all degenerate edges that are connected to
+ // non-degenerate edges. (Any remaining duplicate edges can
+ // be merged using DuplicateEdges::MERGE.) This is useful
+ // for simplifying polygons while ensuring that loops that
+ // collapse to a single point do not disappear.
+ //
+ // KEEP: Keeps all degenerate edges. Be aware that this may create many
+ // redundant edges when simplifying geometry (e.g., a polyline of the
+ // form AABBBBBCCCCCCDDDD). DegenerateEdges::KEEP is mainly useful
+ // for algorithms that require an output edge for every input edge.
+ //
+ // DEFAULT: DegenerateEdges::KEEP
+ enum class DegenerateEdges { DISCARD, DISCARD_EXCESS, KEEP };
+ DegenerateEdges degenerate_edges() const;
+ void set_degenerate_edges(DegenerateEdges degenerate_edges);
+
+ // Controls how duplicate edges (i.e., edges that are present multiple
+ // times) are handled. Such edges may be present in the input, or they can
+ // be created when vertices are snapped together. When several edges are
+ // merged, the result is a single edge labelled with all of the original
+ // input edge ids.
+ //
+ // DEFAULT: DuplicateEdges::KEEP
+ enum class DuplicateEdges { MERGE, KEEP };
+ DuplicateEdges duplicate_edges() const;
+ void set_duplicate_edges(DuplicateEdges duplicate_edges);
+
+ // Controls how sibling edge pairs (i.e., pairs consisting of an edge and
+ // its reverse edge) are handled. Layer types that define an interior
+ // (e.g., polygons) normally discard such edge pairs since they do not
+ // affect the result (i.e., they define a "loop" with no interior). The
+ // various options include:
+ //
+ // DISCARD: Discards all sibling edge pairs.
+ //
+ // DISCARD_EXCESS: Like DISCARD, except that a single sibling pair is kept
+ // if the result would otherwise be empty. This is useful
+ // for polygons with degeneracies (S2LaxPolygonShape), and
+ // for simplifying polylines while ensuring that they are
+ // not split into multiple disconnected pieces.
+ //
+ // KEEP: Keeps sibling pairs. This can be used to create polylines that
+ // double back on themselves, or degenerate loops (with a layer type
+ // such as S2LaxPolygonShape).
+ //
+ // REQUIRE: Requires that all edges have a sibling (and returns an error
+ // otherwise). This is useful with layer types that create a
+ // collection of adjacent polygons (a polygon mesh).
+ //
+ // CREATE: Ensures that all edges have a sibling edge by creating them if
+ // necessary. This is useful with polygon meshes where the input
+ // polygons do not cover the entire sphere. Such edges always
+ // have an empty set of labels.
+ //
+ // If edge_type() is EdgeType::UNDIRECTED, a sibling edge pair is considered
+ // to consist of four edges (two duplicate edges and their siblings), since
+ // only two of these four edges will be used in the final output.
+ //
+ // Furthermore, since the options REQUIRE and CREATE guarantee that all
+ // edges will have siblings, S2Builder implements these options for
+ // undirected edges by discarding half of the edges in each direction and
+ // changing the edge_type() to EdgeType::DIRECTED. For example, two
+ // undirected input edges between vertices A and B would first be converted
+ // into two directed edges in each direction, and then one edge of each pair
+ // would be discarded leaving only one edge in each direction.
+ //
+ // Degenerate edges are considered not to have siblings. If such edges are
+ // present, they are passed through unchanged by SiblingPairs::DISCARD. For
+ // SiblingPairs::REQUIRE or SiblingPairs::CREATE with undirected edges, the
+ // number of copies of each degenerate edge is reduced by a factor of two.
+ //
+ // Any of the options that discard edges (DISCARD, DISCARD_EXCESS, and
+ // REQUIRE/CREATE in the case of undirected edges) have the side effect that
+ // when duplicate edges are present, all of the corresponding edge labels
+ // are merged together and assigned to the remaining edges. (This avoids
+ // the problem of having to decide which edges are discarded.) Note that
+ // this merging takes place even when all copies of an edge are kept, and
+ // that even labels attached to duplicate degenerate edges are merged. For
+ // example, consider the graph {AB1, AB2, BA3, CD4, CD5} (where XYn denotes
+ // an edge from X to Y with label "n"). With SiblingPairs::DISCARD, we need
+ // to discard one of the copies of AB. But which one? Rather than choosing
+ // arbitrarily, instead we merge the labels of all duplicate edges (even
+ // ones where no sibling pairs were discarded), yielding {AB12, CD45, CD45}
+ // (assuming that duplicate edges are being kept).
+ //
+ // DEFAULT: SiblingPairs::KEEP
+ enum class SiblingPairs { DISCARD, DISCARD_EXCESS, KEEP, REQUIRE, CREATE };
+ SiblingPairs sibling_pairs() const;
+ void set_sibling_pairs(SiblingPairs sibling_pairs);
+
+ // This is a specialized option that is only needed by clients want to work
+ // with the graphs for multiple layers at the same time (e.g., in order to
+ // check whether the same edge is present in two different graphs). [Note
+ // that if you need to do this, usually it is easier just to build a single
+ // graph with suitable edge labels.]
+ //
+ // When there are a large number of layers, then by default S2Builder builds
+ // a minimal subgraph for each layer containing only the vertices needed by
+ // the edges in that layer. This ensures that layer types that iterate over
+ // the vertices run in time proportional to the size of that layer rather
+ // than the size of all layers combined. (For example, if there are a
+ // million layers with one edge each, then each layer would be passed a
+ // graph with 2 vertices rather than 2 million vertices.)
+ //
+ // If this option is set to false, this optimization is disabled. Instead
+ // the graph passed to this layer will contain the full set of vertices.
+ // (This is not recommended when the number of layers could be large.)
+ //
+ // DEFAULT: true
+ bool allow_vertex_filtering() const;
+ void set_allow_vertex_filtering(bool allow_vertex_filtering);
+
+ private:
+ EdgeType edge_type_;
+ DegenerateEdges degenerate_edges_;
+ DuplicateEdges duplicate_edges_;
+ SiblingPairs sibling_pairs_;
+ bool allow_vertex_filtering_;
+};
+
+bool operator==(const S2Builder::GraphOptions& x,
+ const S2Builder::GraphOptions& y);
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+// The maximum snap radius is just large enough to support snapping to
+// S2CellId level 0. It is equivalent to 7800km on the Earth's surface.
+inline S1Angle S2Builder::SnapFunction::kMaxSnapRadius() {
+ // This value can't be larger than 85.7 degrees without changing the code
+ // related to min_edge_length_to_split_ca_, and increasing it to 90 degrees
+ // or more would most likely require significant changes to the algorithm.
+ return S1Angle::Degrees(70);
+}
+
+inline const S2Builder::SnapFunction& S2Builder::Options::snap_function()
+ const {
+ return *snap_function_;
+}
+
+inline void S2Builder::Options::set_snap_function(
+ const SnapFunction& snap_function) {
+ snap_function_ = snap_function.Clone();
+}
+
+inline bool S2Builder::Options::split_crossing_edges() const {
+ return split_crossing_edges_;
+}
+
+inline void S2Builder::Options::set_split_crossing_edges(
+ bool split_crossing_edges) {
+ split_crossing_edges_ = split_crossing_edges;
+}
+
+inline bool S2Builder::Options::simplify_edge_chains() const {
+ return simplify_edge_chains_;
+}
+
+inline void S2Builder::Options::set_simplify_edge_chains(
+ bool simplify_edge_chains) {
+ simplify_edge_chains_ = simplify_edge_chains;
+
+ // Simplification requires a non-zero snap radius, and while it might be
+ // possible to do some simplifying without snapping, it is much simpler to
+ // always snap (even if the input geometry already meets the other output
+ // requirements). We need to compute edge_sites_ in order to avoid
+ // approaching non-incident vertices too closely, for example.
+ set_idempotent(false);
+}
+
+inline bool S2Builder::Options::idempotent() const {
+ return idempotent_;
+}
+
+inline void S2Builder::Options::set_idempotent(bool idempotent) {
+ idempotent_ = idempotent;
+}
+
+inline S2Builder::GraphOptions::EdgeType
+S2Builder::GraphOptions::edge_type() const {
+ return edge_type_;
+}
+
+inline void S2Builder::GraphOptions::set_edge_type(EdgeType edge_type) {
+ edge_type_ = edge_type;
+}
+
+inline S2Builder::GraphOptions::DegenerateEdges
+S2Builder::GraphOptions::degenerate_edges() const {
+ return degenerate_edges_;
+}
+
+inline void S2Builder::GraphOptions::set_degenerate_edges(
+ DegenerateEdges degenerate_edges) {
+ degenerate_edges_ = degenerate_edges;
+}
+
+inline S2Builder::GraphOptions::DuplicateEdges
+S2Builder::GraphOptions::duplicate_edges() const {
+ return duplicate_edges_;
+}
+
+inline void S2Builder::GraphOptions::set_duplicate_edges(
+ DuplicateEdges duplicate_edges) {
+ duplicate_edges_ = duplicate_edges;
+}
+
+inline S2Builder::GraphOptions::SiblingPairs
+S2Builder::GraphOptions::sibling_pairs() const {
+ return sibling_pairs_;
+}
+
+inline void S2Builder::GraphOptions::set_sibling_pairs(
+ SiblingPairs sibling_pairs) {
+ sibling_pairs_ = sibling_pairs;
+}
+
+inline bool S2Builder::GraphOptions::allow_vertex_filtering() const {
+ return allow_vertex_filtering_;
+}
+
+inline void S2Builder::GraphOptions::set_allow_vertex_filtering(
+ bool allow_vertex_filtering) {
+ allow_vertex_filtering_ = allow_vertex_filtering;
+}
+
+inline bool S2Builder::is_forced(SiteId v) const {
+ return v < num_forced_sites_;
+}
+
+inline void S2Builder::AddPoint(const S2Point& v) {
+ AddEdge(v, v);
+}
+
+#endif // S2_S2BUILDER_H_
--- /dev/null
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2BUILDER_GRAPH_H_
+#define S2_S2BUILDER_GRAPH_H_
+
+#include <array>
+#include <cstddef>
+#include <iterator>
+#include <utility>
+#include <vector>
+#include "s2/base/integral_types.h"
+#include "s2/id_set_lexicon.h"
+#include "s2/s2builder.h"
+#include "s2/s2error.h"
+
+// An S2Builder::Graph represents a collection of snapped edges that is passed
+// to a Layer for assembly. (Example layers include polygons, polylines, and
+// polygon meshes.) The Graph object does not own any of its underlying data;
+// it is simply a view of data that is stored elsewhere. You will only
+// need this interface if you want to implement a new Layer subtype.
+//
+// The graph consists of vertices and directed edges. Vertices are numbered
+// sequentially starting from zero. An edge is represented as a pair of
+// vertex ids. The edges are sorted in lexicographic order, therefore all of
+// the outgoing edges from a particular vertex form a contiguous range.
+//
+// S2Builder::Graph is movable and copyable. Note that although this class
+// does not own the underlying vertex and edge data, S2Builder guarantees that
+// all Graph objects passed to S2Builder::Layer::Build() methods will remain
+// valid until all layers have been built.
+//
+// TODO(ericv): Consider pulling out the methods that are helper functions for
+// Layer implementations (such as GetDirectedLoops) into s2builderutil_graph.h.
+class S2Builder::Graph {
+ public:
+ // Identifies a vertex in the graph. Vertices are numbered sequentially
+ // starting from zero.
+ using VertexId = int32;
+
+ // Defines an edge as an (origin, destination) vertex pair.
+ using Edge = std::pair<VertexId, VertexId>;
+
+ // Identifies an edge in the graph. Edges are numbered sequentially
+ // starting from zero.
+ using EdgeId = int32;
+
+ // Identifies an S2Builder *input* edge (before snapping).
+ using InputEdgeId = S2Builder::InputEdgeId;
+
+ // Identifies a set of S2Builder input edges.
+ using InputEdgeIdSetId = S2Builder::InputEdgeIdSetId;
+
+ // Identifies a set of edge labels.
+ using LabelSetId = S2Builder::LabelSetId;
+
+ // Determines whether a degenerate polygon is empty or full.
+ using IsFullPolygonPredicate = S2Builder::IsFullPolygonPredicate;
+
+ // The default constructor exists only for the benefit of STL containers.
+ // The graph must be initialized (using the assignment operator) before it
+ // is used.
+ Graph();
+
+ // Note that most of the parameters are passed by const reference and must
+ // exist for the duration of the Graph object. Notes on parameters:
+ // "options":
+ // - the GraphOptions used to build the Graph. In some cases these
+ // can be different than the options provided by the Layer.
+ // "vertices":
+ // - a vector of S2Points indexed by VertexId.
+ // "edges":
+ // - a vector of VertexId pairs (sorted in lexicographic order)
+ // indexed by EdgeId.
+ // "input_edge_id_set_ids":
+ // - a vector indexed by EdgeId that allows access to the set of
+ // InputEdgeIds that were mapped to the given edge, by looking up the
+ // returned value (an InputEdgeIdSetId) in "input_edge_id_set_lexicon".
+ // "input_edge_id_set_lexicon":
+ // - a class that maps an InputEdgeIdSetId to a set of InputEdgeIds.
+ // "label_set_ids":
+ // - a vector indexed by InputEdgeId that allows access to the set of
+ // labels that were attached to the given input edge, by looking up the
+ // returned value (a LabelSetId) in the "label_set_lexicon".
+ // "label_set_lexicon":
+ // - a class that maps a LabelSetId to a set of S2Builder::Labels.
+ // "is_full_polygon_predicate":
+ // - a predicate called to determine whether a graph consisting only of
+ // polygon degeneracies represents the empty polygon or the full polygon
+ // (see s2builder.h for details).
+ Graph(const GraphOptions& options,
+ const std::vector<S2Point>* vertices,
+ const std::vector<Edge>* edges,
+ const std::vector<InputEdgeIdSetId>* input_edge_id_set_ids,
+ const IdSetLexicon* input_edge_id_set_lexicon,
+ const std::vector<LabelSetId>* label_set_ids,
+ const IdSetLexicon* label_set_lexicon,
+ IsFullPolygonPredicate is_full_polygon_predicate);
+
+ const GraphOptions& options() const;
+
+ // Returns the number of vertices in the graph.
+ VertexId num_vertices() const;
+
+ // Returns the vertex at the given index.
+ const S2Point& vertex(VertexId v) const;
+
+ // Returns the entire set of vertices.
+ const std::vector<S2Point>& vertices() const;
+
+ // Returns the total number of edges in the graph.
+ EdgeId num_edges() const;
+
+ // Returns the endpoints of the given edge (as vertex indices).
+ const Edge& edge(EdgeId e) const;
+
+ // Returns the entire set of edges.
+ const std::vector<Edge>& edges() const;
+
+ // Given an edge (src, dst), returns the reverse edge (dst, src).
+ static Edge reverse(const Edge& e);
+
+ // Returns a vector of edge ids sorted in lexicographic order by
+ // (destination, origin). All of the incoming edges to each vertex form a
+ // contiguous subrange of this ordering.
+ std::vector<EdgeId> GetInEdgeIds() const;
+
+ // Given a graph such that every directed edge has a sibling, returns a map
+ // from EdgeId to the sibling EdgeId. This method is identical to
+ // GetInEdgeIds() except that (1) it requires edges to have siblings, and
+ // (2) undirected degenerate edges are grouped together in pairs such that
+ // one edge is the sibling of the other. Handles duplicate edges correctly
+ // and is also consistent with GetLeftTurnMap().
+ //
+ // REQUIRES: An option is chosen that guarantees sibling pairs:
+ // (options.sibling_pairs() == { REQUIRE, CREATE } ||
+ // options.edge_type() == UNDIRECTED)
+ std::vector<EdgeId> GetSiblingMap() const;
+
+ // Like GetSiblingMap(), but constructs the map starting from the vector of
+ // incoming edge ids returned by GetInEdgeIds(). (This operation is a no-op
+ // except unless undirected degenerate edges are present, in which case such
+ // edges are grouped together in pairs to satisfy the requirement that every
+ // edge must have a sibling edge.)
+ void MakeSiblingMap(std::vector<EdgeId>* in_edge_ids) const;
+
+ class VertexOutMap; // Forward declaration
+ class VertexInMap; // Forward declaration
+
+ // A helper class for VertexOutMap that represents the outgoing edges
+ // from a given vertex.
+ class VertexOutEdges {
+ public:
+ const Edge* begin() const { return begin_; }
+ const Edge* end() const { return end_; }
+ size_t size() const { return end_ - begin_; }
+
+ private:
+ friend class VertexOutMap;
+ VertexOutEdges(const Edge* begin, const Edge* end);
+ const Edge* begin_;
+ const Edge* end_;
+ };
+
+ // A helper class for VertexOutMap that represents the outgoing edge *ids*
+ // from a given vertex.
+ class VertexOutEdgeIds
+ : public std::iterator<std::forward_iterator_tag, EdgeId> {
+ public:
+ // An iterator over a range of edge ids (like boost::counting_iterator).
+ class Iterator {
+ public:
+ explicit Iterator(EdgeId id) : id_(id) {}
+ const EdgeId& operator*() const { return id_; }
+ Iterator& operator++() { ++id_; return *this; }
+ Iterator operator++(int) { return Iterator(id_++); }
+ size_t operator-(const Iterator& x) const { return id_ - x.id_; }
+ bool operator==(const Iterator& x) const { return id_ == x.id_; }
+ bool operator!=(const Iterator& x) const { return id_ != x.id_; }
+
+ private:
+ EdgeId id_;
+ };
+ Iterator begin() const { return Iterator(begin_); }
+ Iterator end() const { return Iterator(end_); }
+ size_t size() const { return end_ - begin_; }
+
+ private:
+ friend class VertexOutMap;
+ VertexOutEdgeIds(EdgeId begin, EdgeId end);
+ EdgeId begin_, end_;
+ };
+
+ // A class that maps vertices to their outgoing edge ids. Example usage:
+ // VertexOutMap out(g);
+ // for (Graph::EdgeId e : out.edge_ids(v)) { ... }
+ // for (const Graph::Edge& edge : out.edges(v)) { ... }
+ class VertexOutMap {
+ public:
+ VertexOutMap() = default;
+ explicit VertexOutMap(const Graph& g) { Init(g); }
+ void Init(const Graph& g);
+
+ int degree(VertexId v) const;
+ VertexOutEdges edges(VertexId v) const;
+ VertexOutEdgeIds edge_ids(VertexId v) const;
+
+ // Return the edges (or edge ids) between a specific pair of vertices.
+ VertexOutEdges edges(VertexId v0, VertexId v1) const;
+ VertexOutEdgeIds edge_ids(VertexId v0, VertexId v1) const;
+
+ private:
+ const std::vector<Edge>* edges_;
+ std::vector<EdgeId> edge_begins_;
+ VertexOutMap(const VertexOutMap&) = delete;
+ void operator=(const VertexOutMap&) = delete;
+ };
+
+ // A helper class for VertexInMap that represents the incoming edge *ids*
+ // to a given vertex.
+ class VertexInEdgeIds {
+ public:
+ const EdgeId* begin() const { return begin_; }
+ const EdgeId* end() const { return end_; }
+ size_t size() const { return end_ - begin_; }
+
+ private:
+ friend class VertexInMap;
+ VertexInEdgeIds(const EdgeId* begin, const EdgeId* end);
+ const EdgeId* begin_;
+ const EdgeId* end_;
+ };
+
+ // A class that maps vertices to their incoming edge ids. Example usage:
+ // VertexInMap in(g);
+ // for (Graph::EdgeId e : in.edge_ids(v)) { ... }
+ class VertexInMap {
+ public:
+ VertexInMap() = default;
+ explicit VertexInMap(const Graph& g) { Init(g); }
+ void Init(const Graph& g);
+
+ int degree(VertexId v) const;
+ VertexInEdgeIds edge_ids(VertexId v) const;
+
+ // Returns a sorted vector of all incoming edges (see GetInEdgeIds).
+ const std::vector<EdgeId>& in_edge_ids() const { return in_edge_ids_; }
+
+ private:
+ std::vector<EdgeId> in_edge_ids_;
+ std::vector<EdgeId> in_edge_begins_;
+ VertexInMap(const VertexInMap&) = delete;
+ void operator=(const VertexInMap&) = delete;
+ };
+
+ // Defines a value larger than any valid InputEdgeId.
+ static const InputEdgeId kMaxInputEdgeId =
+ std::numeric_limits<InputEdgeId>::max();
+
+ // The following value of InputEdgeId means that an edge does not
+ // corresponds to any input edge.
+ static const InputEdgeId kNoInputEdgeId = kMaxInputEdgeId - 1;
+
+ // Returns the set of input edge ids that were snapped to the given
+ // edge. ("Input edge ids" are assigned to input edges sequentially in
+ // the order they are added to the builder.) For example, if input
+ // edges 2 and 17 were snapped to edge 12, then input_edge_ids(12)
+ // returns a set containing the numbers 2 and 17. Example usage:
+ //
+ // for (InputEdgeId input_edge_id : g.input_edge_ids(e)) { ... }
+ //
+ // Please note the following:
+ //
+ // - When edge chains are simplified, the simplified edge is assigned all
+ // the input edge ids associated with edges of the chain.
+ //
+ // - Edges can also have multiple input edge ids due to edge merging
+ // (if DuplicateEdges::MERGE is specified).
+ //
+ // - Siblings edges automatically created by EdgeType::UNDIRECTED or
+ // SiblingPairs::CREATE have an empty set of input edge ids. (However
+ // you can use a LabelFetcher to retrieve the set of labels associated
+ // with both edges of a given sibling pair.)
+ IdSetLexicon::IdSet input_edge_ids(EdgeId e) const;
+
+ // Low-level method that returns an integer representing the entire set of
+ // input edge ids that were snapped to the given edge. The elements of the
+ // IdSet can be accessed using input_edge_id_set_lexicon().
+ InputEdgeIdSetId input_edge_id_set_id(EdgeId e) const;
+
+ // Low-level method that returns a vector where each element represents the
+ // set of input edge ids that were snapped to a particular output edge.
+ const std::vector<InputEdgeIdSetId>& input_edge_id_set_ids() const;
+
+ // Returns a mapping from an InputEdgeIdSetId to a set of input edge ids.
+ const IdSetLexicon& input_edge_id_set_lexicon() const;
+
+ // Returns the minimum input edge id that was snapped to this edge, or -1 if
+ // no input edges were snapped (see SiblingPairs::CREATE). This is
+ // useful for layers that wish to preserve the input edge ordering as much
+ // as possible (e.g., to ensure idempotency).
+ InputEdgeId min_input_edge_id(EdgeId e) const;
+
+ // Returns a vector containing the minimum input edge id for every edge.
+ // If an edge has no input ids, kNoInputEdgeId is used.
+ std::vector<InputEdgeId> GetMinInputEdgeIds() const;
+
+ // Returns a vector of EdgeIds sorted by minimum input edge id. This is an
+ // approximation of the input edge ordering.
+ std::vector<EdgeId> GetInputEdgeOrder(
+ const std::vector<InputEdgeId>& min_input_edge_ids) const;
+
+ // Convenience class to return the set of labels associated with a given
+ // graph edge. Note that due to snapping, one graph edge may correspond to
+ // several different input edges and will have all of their labels.
+ // This class is the preferred way to retrieve edge labels.
+ //
+ // The reason this is a class rather than a graph method is because for
+ // undirected edges, we need to fetch the labels associated with both
+ // siblings. This is because only the original edge of the sibling pair has
+ // labels; the automatically generated sibling edge does not.
+ class LabelFetcher {
+ public:
+ LabelFetcher() = default;
+ LabelFetcher(const Graph& g, EdgeType edge_type) { Init(g, edge_type); }
+
+ // Prepares to fetch labels associated with the given edge type. For
+ // EdgeType::UNDIRECTED, labels associated with both edges of the sibling
+ // pair will be returned. "edge_type" is a parameter (rather than using
+ // g.options().edge_type()) so that clients can explicitly control whether
+ // labels from one or both siblings are returned.
+ void Init(const Graph& g, EdgeType edge_type);
+
+ // Returns the set of labels associated with edge "e" (and also the labels
+ // associated with the sibling of "e" if edge_type() is UNDIRECTED).
+ // Labels are sorted and duplicate labels are automatically removed.
+ //
+ // This method uses an output parameter rather than returning by value in
+ // order to avoid allocating a new vector on every call to this method.
+ void Fetch(EdgeId e, std::vector<S2Builder::Label>* labels);
+
+ private:
+ const Graph* g_;
+ EdgeType edge_type_;
+ std::vector<EdgeId> sibling_map_;
+ };
+
+ // Returns the set of labels associated with a given input edge. Example:
+ // for (Label label : g.labels(input_edge_id)) { ... }
+ IdSetLexicon::IdSet labels(InputEdgeId e) const;
+
+ // Low-level method that returns an integer representing the set of
+ // labels associated with a given input edge. The elements of
+ // the IdSet can be accessed using label_set_lexicon().
+ LabelSetId label_set_id(InputEdgeId e) const;
+
+ // Low-level method that returns a vector where each element represents the
+ // set of labels associated with a particular output edge.
+ const std::vector<LabelSetId>& label_set_ids() const;
+
+ // Returns a mapping from a LabelSetId to a set of labels.
+ const IdSetLexicon& label_set_lexicon() const;
+
+ // Convenience method that calls is_full_polygon_predicate() to determine
+ // whether a graph that consists only of polygon degeneracies represents the
+ // empty polygon or the full polygon (see s2builder.h for details).
+ bool IsFullPolygon(S2Error* error) const;
+
+ // Returns a method that determines whether a graph that consists only of
+ // polygon degeneracies represents the empty polygon or the full polygon
+ // (see s2builder.h for details).
+ const IsFullPolygonPredicate& is_full_polygon_predicate() const;
+
+ // Returns a map "m" that maps each edge e=(v0,v1) to the following outgoing
+ // edge around "v1" in clockwise order. (This corresponds to making a "left
+ // turn" at the vertex.) By starting at a given edge and making only left
+ // turns, you can construct a loop whose interior does not contain any edges
+ // in the same connected component.
+ //
+ // If the incoming and outgoing edges around a vertex do not alternate
+ // perfectly (e.g., there are two incoming edges in a row), then adjacent
+ // (incoming, outgoing) pairs are repeatedly matched and removed. This is
+ // similar to finding matching parentheses in a string such as "(()())()".
+ //
+ // For sibling edge pairs, the incoming edge is assumed to immediately
+ // follow the outgoing edge in clockwise order. Thus a left turn is made
+ // from an edge to its sibling only if there are no other outgoing edges.
+ // With respect to the parentheses analogy, a sibling pair is ")(".
+ // Similarly, if there are multiple copies of a sibling edge pair then the
+ // duplicate incoming and outgoing edges are sorted in alternating order
+ // (e.g., ")()(").
+ //
+ // Degenerate edges (edges from a vertex to itself) are treated as loops
+ // consisting of a single edge. This avoids the problem of deciding the
+ // connectivity and ordering of such edges when they share a vertex with
+ // other edges (possibly including other degenerate edges).
+ //
+ // If it is not possible to make a left turn from every input edge, this
+ // method returns false and sets "error" appropriately. In this situation
+ // the left turn map is still valid except that any incoming edge where it
+ // is not possible to make a left turn will have its entry set to -1.
+ //
+ // "in_edge_ids" should be equal to GetInEdgeIds() or GetSiblingMap().
+ bool GetLeftTurnMap(const std::vector<EdgeId>& in_edge_ids,
+ std::vector<EdgeId>* left_turn_map,
+ S2Error* error) const;
+
+ // Rotates the edges of "loop" if necessary so that the edge(s) with the
+ // largest input edge ids are last. This ensures that when an output loop
+ // is equivalent to an input loop, their cyclic edge orders are the same.
+ // "min_input_ids" is the output of GetMinInputEdgeIds().
+ static void CanonicalizeLoopOrder(
+ const std::vector<InputEdgeId>& min_input_ids,
+ std::vector<EdgeId>* loop);
+
+ // Sorts the given edge chains (i.e., loops or polylines) by the minimum
+ // input edge id of each chains's first edge. This ensures that when the
+ // output consists of multiple loops or polylines, they are sorted in the
+ // same order as they were provided in the input.
+ static void CanonicalizeVectorOrder(
+ const std::vector<InputEdgeId>& min_input_ids,
+ std::vector<std::vector<EdgeId>>* chains);
+
+ // A loop consisting of a sequence of edges.
+ using EdgeLoop = std::vector<EdgeId>;
+
+ // Indicates whether loops should be simple cycles (no repeated vertices) or
+ // circuits (which allow repeated vertices but not repeated edges). In
+ // terms of how the loops are built, this corresponds to closing off a loop
+ // at the first repeated vertex vs. the first repeated edge.
+ enum class LoopType { SIMPLE, CIRCUIT };
+
+ // Builds loops from a set of directed edges, turning left at each vertex
+ // until either a repeated vertex (for LoopType::SIMPLE) or a repeated edge
+ // (for LoopType::CIRCUIT) is found. (Use LoopType::SIMPLE if you intend to
+ // construct an S2Loop.)
+ //
+ // Each loop is represented as a sequence of edges. The edge ordering and
+ // loop ordering are automatically canonicalized in order to preserve the
+ // input ordering as much as possible. Loops are non-crossing provided that
+ // the graph contains no crossing edges. If some edges cannot be turned
+ // into loops, returns false and sets "error" appropriately.
+ //
+ // If any degenerate edges are present, then each such edge is treated as a
+ // separate loop. This is mainly useful in conjunction with
+ // options.degenerate_edges() == DISCARD_EXCESS, in order to build polygons
+ // that preserve degenerate geometry.
+ //
+ // REQUIRES: options.degenerate_edges() == {DISCARD, DISCARD_EXCESS}
+ // REQUIRES: options.edge_type() == DIRECTED
+ bool GetDirectedLoops(LoopType loop_type, std::vector<EdgeLoop>* loops,
+ S2Error* error) const;
+
+ // Builds loops from a set of directed edges, turning left at each vertex
+ // until a repeated edge is found (i.e., LoopType::CIRCUIT). The loops are
+ // further grouped into connected components, where each component consists
+ // of one or more loops connected by shared vertices.
+ //
+ // This method is used to build polygon meshes from directed or undirected
+ // input edges. To convert the output of this method into a mesh, the
+ // client must determine how the loops in different components are related
+ // to each other: for example, several loops from different components may
+ // bound the same region on the sphere, in which case all of those loops are
+ // combined into a single polygon. (See s2shapeutil::BuildPolygonBoundaries
+ // and s2builderutil::LaxPolygonVectorLayer for details.)
+ //
+ // Note that loops may include both edges of a sibling pair. When several
+ // such edges are connected in a chain or a spanning tree, they form a
+ // zero-area "filament". The entire loop may be a filament (i.e., a
+ // degenerate loop with an empty interior), or the loop may have have
+ // non-empty interior with several filaments that extend inside it, or the
+ // loop may consist of several "holes" connected by filaments. These
+ // filaments do not change the interior of any loop, so if you are only
+ // interested in point containment then they can safely be removed by
+ // setting the "degenerate_boundaries" parameter to DISCARD. (They can't be
+ // removed by setting (options.sibling_pairs() == DISCARD) because the two
+ // siblings might belong to different polygons of the mesh.) Note that you
+ // can prevent multiple copies of sibling pairs by specifying
+ // options.duplicate_edges() == MERGE.
+ //
+ // Each loop is represented as a sequence of edges. The edge ordering and
+ // loop ordering are automatically canonicalized in order to preserve the
+ // input ordering as much as possible. Loops are non-crossing provided that
+ // the graph contains no crossing edges. If some edges cannot be turned
+ // into loops, returns false and sets "error" appropriately.
+ //
+ // REQUIRES: options.degenerate_edges() == { DISCARD, DISCARD_EXCESS }
+ // (but requires DISCARD if degenerate_boundaries == DISCARD)
+ // REQUIRES: options.sibling_pairs() == { REQUIRE, CREATE }
+ // [i.e., every edge must have a sibling edge]
+ enum class DegenerateBoundaries { DISCARD, KEEP };
+ using DirectedComponent = std::vector<EdgeLoop>;
+ bool GetDirectedComponents(
+ DegenerateBoundaries degenerate_boundaries,
+ std::vector<DirectedComponent>* components, S2Error* error) const;
+
+ // Builds loops from a set of undirected edges, turning left at each vertex
+ // until either a repeated vertex (for LoopType::SIMPLE) or a repeated edge
+ // (for LoopType::CIRCUIT) is found. The loops are further grouped into
+ // "components" such that all the loops in a component are connected by
+ // shared vertices. Finally, the loops in each component are divided into
+ // two "complements" such that every edge in one complement is the sibling
+ // of an edge in the other complement. This corresponds to the fact that
+ // given any set of non-crossing undirected loops, there are exactly two
+ // possible interpretations of the region that those loops represent (where
+ // one possibility is the complement of the other). This method does not
+ // attempt to resolve this ambiguity, but instead returns both possibilities
+ // for each connected component and lets the client choose among them.
+ //
+ // This method is used to build single polygons. (Use GetDirectedComponents
+ // to build polygon meshes, even when the input edges are undirected.) To
+ // convert the output of this method into a polygon, the client must choose
+ // one complement from each component such that the entire set of loops is
+ // oriented consistently (i.e., they define a region such that the interior
+ // of the region is always on the left). The non-chosen complements form
+ // another set of loops that are also oriented consistently but represent
+ // the complementary region on the sphere. Finally, the client needs to
+ // choose one of these two sets of loops based on heuristics (e.g., the area
+ // of each region), since both sets of loops are equally valid
+ // interpretations of the input.
+ //
+ // Each loop is represented as a sequence of edges. The edge ordering and
+ // loop ordering are automatically canonicalized in order to preserve the
+ // input ordering as much as possible. Loops are non-crossing provided that
+ // the graph contains no crossing edges. If some edges cannot be turned
+ // into loops, returns false and sets "error" appropriately.
+ //
+ // REQUIRES: options.degenerate_edges() == { DISCARD, DISCARD_EXCESS }
+ // REQUIRES: options.edge_type() == UNDIRECTED
+ // REQUIRES: options.siblings_pairs() == { DISCARD, DISCARD_EXCESS, KEEP }
+ // [since REQUIRE, CREATE convert the edge_type() to DIRECTED]
+ using UndirectedComponent = std::array<std::vector<EdgeLoop>, 2>;
+ bool GetUndirectedComponents(LoopType loop_type,
+ std::vector<UndirectedComponent>* components,
+ S2Error* error) const;
+
+ // Indicates whether polylines should be "paths" (which don't allow
+ // duplicate vertices, except possibly the first and last vertex) or
+ // "walks" (which allow duplicate vertices and edges).
+ enum class PolylineType { PATH, WALK };
+
+ // Builds polylines from a set of edges. If "polyline_type" is PATH, then
+ // only vertices of indegree and outdegree 1 (or degree 2 in the case of
+ // undirected edges) will appear in the interior of polylines. This
+ // essentially generates one polyline for each edge chain in the graph. If
+ // "polyline_type" is WALK, then polylines may pass through the same vertex
+ // or even the same edge multiple times (if duplicate edges are present),
+ // and each polyline will be as long as possible. This option is useful for
+ // reconstructing a polyline that has been snapped to a lower resolution,
+ // since snapping can cause edges to become identical.
+ //
+ // This method attempts to preserve the input edge ordering in order to
+ // implement idempotency, even when there are repeated edges or loops. This
+ // is true whether directed or undirected edges are used. Degenerate edges
+ // are also handled appropriately.
+ //
+ // REQUIRES: options.sibling_pairs() == { DISCARD, DISCARD_EXCESS, KEEP }
+ using EdgePolyline = std::vector<EdgeId>;
+ std::vector<EdgePolyline> GetPolylines(PolylineType polyline_type) const;
+
+ ////////////////////////////////////////////////////////////////////////
+ //////////////// Helper Functions for Creating Graphs //////////////////
+
+ // Given an unsorted collection of edges, transform them according to the
+ // given set of GraphOptions. This includes actions such as discarding
+ // degenerate edges; merging duplicate edges; and canonicalizing sibling
+ // edge pairs in several possible ways (e.g. discarding or creating them).
+ // The output is suitable for passing to the Graph constructor.
+ //
+ // If options.edge_type() == EdgeType::UNDIRECTED, then all input edges
+ // should already have been transformed into a pair of directed edges.
+ //
+ // "input_ids" is a vector of the same length as "edges" that indicates
+ // which input edges were snapped to each edge. This vector is also updated
+ // appropriately as edges are discarded, merged, etc.
+ //
+ // Note that "options" may be modified by this method: in particular, the
+ // edge_type() can be changed if sibling_pairs() is CREATE or REQUIRE (see
+ // the description of S2Builder::GraphOptions).
+ static void ProcessEdges(
+ GraphOptions* options, std::vector<Edge>* edges,
+ std::vector<InputEdgeIdSetId>* input_ids, IdSetLexicon* id_set_lexicon,
+ S2Error* error);
+
+ // Given a set of vertices and edges, removes all vertices that do not have
+ // any edges and returned the new, minimal set of vertices. Also updates
+ // each edge in "edges" to correspond to the new vertex numbering. (Note
+ // that this method does *not* merge duplicate vertices, it simply removes
+ // vertices of degree zero.)
+ //
+ // The new vertex ordering is a subsequence of the original ordering,
+ // therefore if the edges were lexicographically sorted before calling this
+ // method then they will still be sorted after calling this method.
+ //
+ // The extra argument "tmp" points to temporary storage used by this method.
+ // All calls to this method from a single thread can reuse the same
+ // temporary storage. It should initially point to an empty vector. This
+ // can make a big difference to efficiency when this method is called many
+ // times (e.g. to extract the vertices for different layers), since the
+ // incremental running time for each layer becomes O(edges.size()) rather
+ // than O(vertices.size() + edges.size()).
+ static std::vector<S2Point> FilterVertices(
+ const std::vector<S2Point>& vertices, std::vector<Edge>* edges,
+ std::vector<VertexId>* tmp);
+
+ // A comparison function that allows stable sorting with std::sort (which is
+ // fast but not stable). It breaks ties between equal edges by comparing
+ // their edge ids.
+ static bool StableLessThan(const Edge& a, const Edge& b,
+ EdgeId ai, EdgeId bi);
+
+ private:
+ class EdgeProcessor;
+ class PolylineBuilder;
+
+ GraphOptions options_;
+ VertexId num_vertices_; // Cached to avoid division by 24.
+
+ const std::vector<S2Point>* vertices_;
+ const std::vector<Edge>* edges_;
+ const std::vector<InputEdgeIdSetId>* input_edge_id_set_ids_;
+ const IdSetLexicon* input_edge_id_set_lexicon_;
+ const std::vector<LabelSetId>* label_set_ids_;
+ const IdSetLexicon* label_set_lexicon_;
+ IsFullPolygonPredicate is_full_polygon_predicate_;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline S2Builder::Graph::Graph()
+ : options_(), num_vertices_(-1), vertices_(nullptr), edges_(nullptr),
+ input_edge_id_set_ids_(nullptr), input_edge_id_set_lexicon_(nullptr),
+ label_set_ids_(nullptr), label_set_lexicon_(nullptr) {
+}
+
+inline const S2Builder::GraphOptions& S2Builder::Graph::options() const {
+ return options_;
+}
+
+inline S2Builder::Graph::VertexId S2Builder::Graph::num_vertices() const {
+ return num_vertices_; // vertices_.size() requires division by 24.
+}
+
+inline const S2Point& S2Builder::Graph::vertex(VertexId v) const {
+ return vertices()[v];
+}
+
+inline const std::vector<S2Point>& S2Builder::Graph::vertices() const {
+ return *vertices_;
+}
+
+inline S2Builder::Graph::EdgeId S2Builder::Graph::num_edges() const {
+ return static_cast<S2Builder::Graph::EdgeId>(edges().size());
+}
+
+inline const S2Builder::Graph::Edge& S2Builder::Graph::edge(EdgeId e) const {
+ return edges()[e];
+}
+
+inline const std::vector<S2Builder::Graph::Edge>&
+S2Builder::Graph::edges() const {
+ return *edges_;
+}
+
+inline S2Builder::Graph::Edge S2Builder::Graph::reverse(const Edge& e) {
+ return Edge(e.second, e.first);
+}
+
+inline S2Builder::Graph::VertexOutEdges::VertexOutEdges(const Edge* begin,
+ const Edge* end)
+ : begin_(begin), end_(end) {
+}
+
+inline S2Builder::Graph::VertexOutEdges
+S2Builder::Graph::VertexOutMap::edges(VertexId v) const {
+ return VertexOutEdges(edges_->data() + edge_begins_[v],
+ edges_->data() + edge_begins_[v + 1]);
+}
+
+inline S2Builder::Graph::VertexOutEdges
+S2Builder::Graph::VertexOutMap::edges(VertexId v0, VertexId v1) const {
+ auto range = std::equal_range(edges_->data() + edge_begins_[v0],
+ edges_->data() + edge_begins_[v0 + 1],
+ Edge(v0, v1));
+ return VertexOutEdges(range.first, range.second);
+}
+
+inline S2Builder::Graph::VertexOutEdgeIds::VertexOutEdgeIds(EdgeId begin,
+ EdgeId end)
+ : begin_(begin), end_(end) {
+}
+
+inline S2Builder::Graph::VertexOutEdgeIds
+S2Builder::Graph::VertexOutMap::edge_ids(VertexId v) const {
+ return VertexOutEdgeIds(edge_begins_[v], edge_begins_[v + 1]);
+}
+
+inline S2Builder::Graph::VertexOutEdgeIds
+S2Builder::Graph::VertexOutMap::edge_ids(VertexId v0, VertexId v1) const {
+ auto range = std::equal_range(edges_->data() + edge_begins_[v0],
+ edges_->data() + edge_begins_[v0 + 1],
+ Edge(v0, v1));
+ return VertexOutEdgeIds(
+ static_cast<S2Builder::Graph::EdgeId>(range.first - edges_->data()),
+ static_cast<S2Builder::Graph::EdgeId>(range.second - edges_->data()));
+}
+
+inline int S2Builder::Graph::VertexOutMap::degree(VertexId v) const {
+ return static_cast<int>(edge_ids(v).size());
+}
+
+inline S2Builder::Graph::VertexInEdgeIds::VertexInEdgeIds(const EdgeId* begin,
+ const EdgeId* end)
+ : begin_(begin), end_(end) {
+}
+
+inline S2Builder::Graph::VertexInEdgeIds
+S2Builder::Graph::VertexInMap::edge_ids(VertexId v) const {
+ return VertexInEdgeIds(in_edge_ids_.data() + in_edge_begins_[v],
+ in_edge_ids_.data() + in_edge_begins_[v + 1]);
+}
+
+inline int S2Builder::Graph::VertexInMap::degree(VertexId v) const {
+ return static_cast<int>(edge_ids(v).size());
+}
+
+inline IdSetLexicon::IdSet S2Builder::Graph::input_edge_ids(EdgeId e) const {
+ return input_edge_id_set_lexicon().id_set(input_edge_id_set_ids()[e]);
+}
+
+inline const std::vector<S2Builder::InputEdgeIdSetId>&
+S2Builder::Graph::input_edge_id_set_ids() const {
+ return *input_edge_id_set_ids_;
+}
+
+inline S2Builder::InputEdgeIdSetId
+S2Builder::Graph::input_edge_id_set_id(EdgeId e) const {
+ return input_edge_id_set_ids()[e];
+}
+
+inline const IdSetLexicon& S2Builder::Graph::input_edge_id_set_lexicon() const {
+ return *input_edge_id_set_lexicon_;
+}
+
+inline IdSetLexicon::IdSet S2Builder::Graph::labels(LabelSetId id) const {
+ return label_set_lexicon().id_set(label_set_ids()[id]);
+}
+
+inline S2Builder::LabelSetId S2Builder::Graph::label_set_id(EdgeId e) const {
+ return label_set_ids()[e];
+}
+
+inline const std::vector<S2Builder::LabelSetId>&
+S2Builder::Graph::label_set_ids() const {
+ return *label_set_ids_;
+}
+
+inline const IdSetLexicon& S2Builder::Graph::label_set_lexicon() const {
+ return *label_set_lexicon_;
+}
+
+inline bool S2Builder::Graph::IsFullPolygon(S2Error* error) const {
+ return is_full_polygon_predicate_(*this, error);
+}
+
+inline const S2Builder::IsFullPolygonPredicate&
+S2Builder::Graph::is_full_polygon_predicate() const {
+ return is_full_polygon_predicate_;
+}
+
+inline bool S2Builder::Graph::StableLessThan(
+ const Edge& a, const Edge& b, EdgeId ai, EdgeId bi) {
+ // The following is simpler but the compiler (2016) doesn't optimize it as
+ // well as it should:
+ // return make_pair(a, ai) < make_pair(b, bi);
+ if (a.first < b.first) return true;
+ if (b.first < a.first) return false;
+ if (a.second < b.second) return true;
+ if (b.second < a.second) return false;
+ return ai < bi; // Stable sort.
+}
+
+#endif // S2_S2BUILDER_GRAPH_H_
--- /dev/null
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2BUILDER_LAYER_H_
+#define S2_S2BUILDER_LAYER_H_
+
+#include "s2/s2builder_graph.h"
+
+// This class is not needed by ordinary S2Builder clients. It is only
+// necessary if you wish to implement a new S2Builder::Layer subtype.
+class S2Builder::Layer {
+ public:
+ // Convenience declarations for layer subtypes.
+ using EdgeType = S2Builder::EdgeType;
+ using GraphOptions = S2Builder::GraphOptions;
+ using Graph = S2Builder::Graph;
+ using Label = S2Builder::Label;
+ using LabelSetId = S2Builder::LabelSetId;
+
+ virtual ~Layer() {}
+
+ // Defines options for building the edge graph that is passed to Build().
+ virtual GraphOptions graph_options() const = 0;
+
+ // Assembles a graph of snapped edges into the geometry type implemented by
+ // this layer. If an error is encountered, sets "error" appropriately.
+ //
+ // Note that when there are multiple layers, the Graph objects passed to all
+ // layers are guaranteed to be valid until the last Build() method returns.
+ // This makes it easier to write algorithms that gather the output graphs
+ // from several layers and process them all at once (such as
+ // s2builderutil::ClosedSetNormalizer).
+ virtual void Build(const Graph& g, S2Error* error) = 0;
+};
+
+#endif // S2_S2BUILDER_LAYER_H_
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2BUILDERUTIL_CLOSED_SET_NORMALIZER_H_
+#define S2_S2BUILDERUTIL_CLOSED_SET_NORMALIZER_H_
+
+#include <vector>
+#include "s2/id_set_lexicon.h"
+#include "s2/s2builder_graph.h"
+#include "s2/s2builderutil_find_polygon_degeneracies.h"
+
+namespace s2builderutil {
+
+// The purpose of this class is to allow S2Builder::Layer implementations to
+// remove polygon and polyline degeneracies by converting them to polylines or
+// points. Note that most clients should not use ClosedSetNormalizer itself,
+// but should instead call NormalizeClosedSet defined below.
+//
+// A polyline degeneracy is a polyline consisting of a single degenerate edge.
+// A polygon degeneracy is either a single-vertex loop (a degenerate edge from
+// a vertex to itself) or a sibling edge pair (consisting of an edge and its
+// corresponding reverse edge). Polygon degeneracies are further classified
+// as shells or holes depending on whether they are located in the exterior or
+// interior of the polygon respectively. For example, a single-vertex loop
+// contained within a polygon shell would be classified as a hole.
+//
+// All objects are modeled as closed, i.e. polygons contain their boundaries
+// and polylines contain their endpoints. Note that under this model,
+// degenerate polygon shells and holes need to be handled differently.
+// Degenerate shells are converted to polylines or points, whereas degenerate
+// holes do not affect the set of points contained by the polygon and are
+// simply discarded.
+//
+// Specifically, given three S2Builder::Graphs (corresponding to points,
+// polylines, and polygons), this class makes the following transformations:
+//
+// - Polygon sibling edge pairs are either discarded (for holes) or converted
+// to a pair of polyline edges (for shells).
+// - Degenerate polygon edges are either discarded (for holes) or converted
+// to points (for shells).
+// - Degenerate polyline edges are converted to points.
+//
+// Optionally, this class further normalize the graphs by suppressing edges
+// that are duplicates of higher-dimensional edges. In other words:
+//
+// - Polyline edges that coincide with polygon edges are discarded.
+// - Points that coincide with polyline or polygon vertices are discarded.
+//
+// (When edges are discarded, any labels attached to those edges are discarded
+// as well.)
+//
+// This class takes three graphs as input and yields three graphs as output.
+// However note that the output graphs are *not* independent objects; they may
+// point to data in the input graphs or data owned by the ClosedSetNormalizer
+// itself. For this reason the input graphs and ClosedSetNormalizer must
+// persist until the output graphs are no longer needed.
+//
+// Finally, note that although this class may be necessary in some situations
+// (e.g., to implement the OGC Simple Features Access spec), in general the
+// recommended approach to degeneracies is simply to keep them (by using a
+// representation such as S2LaxPolygonShape or S2LaxPolylineShape).
+// Keeping degeneracies has many advantages, such as not needing to deal with
+// geometry of multiple dimensions, and being able to preserve polygon
+// boundaries accurately (including degenerate holes).
+class ClosedSetNormalizer {
+ public:
+ class Options {
+ public:
+ Options();
+
+ // If "suppress_lower_dimensions" is true, then the graphs are further
+ // normalized by discarding lower-dimensional edges that coincide with
+ // higher-dimensional edges.
+ //
+ // DEFAULT: true
+ bool suppress_lower_dimensions() const;
+ void set_suppress_lower_dimensions(bool suppress_lower_dimensions);
+
+ private:
+ bool suppress_lower_dimensions_;
+ };
+
+ // Constructs a ClosedSetNormalizer whose output will be three
+ // S2Builder::Graphs with the given "graph_options_out".
+ //
+ // REQUIRES: graph_options_out.size() == 3
+ // REQUIRES: graph_options_out[0].edge_type() == DIRECTED
+ // REQUIRES: graph_options_out[1].sibling_pairs() != {CREATE, REQUIRE}
+ // REQUIRES: graph_options_out[2].edge_type() == DIRECTED
+ ClosedSetNormalizer(
+ const Options& options,
+ const std::vector<S2Builder::GraphOptions>& graph_options_out);
+
+ // Returns the ClosedSetNormalizer options.
+ const Options& options() const { return options_; }
+
+ // Returns the GraphOptions that should be used to construct the input
+ // S2Builder::Graph of each dimension.
+ inline const std::vector<S2Builder::GraphOptions>& graph_options() const;
+
+ // Normalizes the input graphs and returns a new set of graphs where
+ // degeneracies have been discarded or converted to objects of lower
+ // dimension. input[d] is the graph representing edges of dimension "d".
+ //
+ // Note that the input graphs, their contents, and the ClosedSetNormalizer
+ // itself must persist until the output of this class is no longer needed.
+ // (To emphasize this requirement, a const reference is returned.)
+ const std::vector<S2Builder::Graph>& Run(
+ const std::vector<S2Builder::Graph>& input, S2Error* error);
+
+ private:
+ S2Builder::Graph::Edge Advance(
+ const S2Builder::Graph& g, S2Builder::Graph::EdgeId* id) const;
+ S2Builder::Graph::Edge AdvanceIncoming(
+ const S2Builder::Graph& g,
+ const std::vector<S2Builder::Graph::EdgeId>& in_edges, int* i) const;
+ void NormalizeEdges(const std::vector<S2Builder::Graph>& g, S2Error* error);
+ void AddEdge(int new_dim, const S2Builder::Graph& g,
+ S2Builder::Graph::EdgeId e);
+ bool is_suppressed(S2Builder::Graph::VertexId v) const;
+
+ Options options_;
+
+ // Requested options for the output graphs.
+ std::vector<S2Builder::GraphOptions> graph_options_out_;
+
+ // Options to be used to construct the input graphs.
+ std::vector<S2Builder::GraphOptions> graph_options_in_;
+
+ // A sentinel value that compares larger than any valid edge.
+ const S2Builder::Graph::Edge sentinel_;
+
+ // is_suppressed_[i] is true if vertex[i] belongs to a non-degenerate edge,
+ // and therefore should be suppressed from the output graph for points.
+ std::vector<bool> is_suppressed_;
+
+ // A vector of incoming polygon edges sorted in lexicographic order. This
+ // is used to suppress directed polyline edges that match a polygon edge in
+ // the reverse direction.
+ std::vector<S2Builder::Graph::EdgeId> in_edges2_;
+
+ // Output data.
+ std::vector<S2Builder::Graph> new_graphs_;
+ std::vector<S2Builder::Graph::Edge> new_edges_[3];
+ std::vector<S2Builder::Graph::InputEdgeIdSetId> new_input_edge_ids_[3];
+ IdSetLexicon new_input_edge_id_set_lexicon_;
+};
+
+// A LayerVector represents a set of layers that comprise a single object.
+// Such objects are typically assembled by gathering the S2Builder::Graphs
+// from all of the individual layers and processing them all at once.
+using LayerVector = std::vector<std::unique_ptr<S2Builder::Layer>>;
+
+// Given a set of three output layers (one each for dimensions 0, 1, and 2),
+// returns a new set of layers that preprocess the input graphs using a
+// ClosedSetNormalizer with the given options. This can be used to ensure
+// that the graphs passed to "output_layers" do not contain any polyline or
+// polygon degeneracies.
+//
+// Example showing how to compute the union of two S2ShapeIndexes containing
+// points, polylines, and/or polygons, and save the result as a collection of
+// S2Points, S2Polylines, and S2Polygons in another S2ShapeIndex (where
+// degeneracies have been normalized to objects of lower dimension, and
+// maximal polylines are constructed from undirected edges):
+//
+// bool ComputeUnion(const S2ShapeIndex& a, const S2ShapeIndex& b,
+// MutableS2ShapeIndex* index, S2Error* error) {
+// IndexedS2PolylineVectorLayer::Options polyline_options;
+// polyline_options.set_edge_type(EdgeType::UNDIRECTED);
+// polyline_options.set_polyline_type(Graph::PolylineType::WALK);
+// polyline_options.set_duplicate_edges(DuplicateEdges::MERGE);
+// LayerVector layers(3);
+// layers[0] = absl::make_unique<IndexedS2PointVectorLayer>(index);
+// layers[1] = absl::make_unique<IndexedS2PolylineVectorLayer>(
+// index, polyline_options);
+// layers[2] = absl::make_unique<IndexedS2PolygonLayer>(index);
+// S2BooleanOperation op(S2BooleanOperation::OpType::UNION,
+// NormalizeClosedSet(std::move(layers)));
+// return op.Build(a, b, error);
+// }
+LayerVector NormalizeClosedSet(LayerVector output_layers,
+ const ClosedSetNormalizer::Options& options =
+ ClosedSetNormalizer::Options());
+
+
+////////////////// Implementation details follow ////////////////////
+
+inline ClosedSetNormalizer::Options::Options()
+ : suppress_lower_dimensions_(true) {}
+
+inline bool ClosedSetNormalizer::Options::suppress_lower_dimensions() const {
+ return suppress_lower_dimensions_;
+}
+
+inline void ClosedSetNormalizer::Options::set_suppress_lower_dimensions(
+ bool suppress_lower_dimensions) {
+ suppress_lower_dimensions_ = suppress_lower_dimensions;
+}
+
+inline const std::vector<S2Builder::GraphOptions>&
+ClosedSetNormalizer::graph_options() const {
+ return graph_options_in_;
+}
+
+} // namespace s2builderutil
+
+#endif // S2_S2BUILDERUTIL_CLOSED_SET_NORMALIZER_H_
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2BUILDERUTIL_FIND_POLYGON_DEGENERACIES_H_
+#define S2_S2BUILDERUTIL_FIND_POLYGON_DEGENERACIES_H_
+
+#include <vector>
+
+#include "s2/base/integral_types.h"
+#include "s2/s2builder_graph.h"
+#include "s2/s2error.h"
+
+namespace s2builderutil {
+
+// A polygon degeneracy is either a degenerate edge (an edge from a vertex to
+// itself) or a sibling edge pair (consisting of an edge and its corresponding
+// reverse edge). "is_hole" indicates whether the degeneracy corresponds to a
+// polygon hole (as opposed to a polygon shell).
+//
+// Degeneracies are not allowed to coincide with any non-degenerate portions
+// of the polygon's boundary (since that would make it impossible to classify
+// the degeneracy as a shell or hole). Specifically, degenerate edges must
+// coincide only with other degenerate edges, and sibling pairs must coincide
+// only with other sibling pairs. (Below we require a slightly stronger
+// condition, namely that sibling pairs cannot coincide with any other edges.)
+struct PolygonDegeneracy {
+ uint32 edge_id : 31;
+ uint32 is_hole : 1;
+
+ PolygonDegeneracy() : edge_id(0), is_hole(false) {}
+ PolygonDegeneracy(S2Builder::Graph::EdgeId _edge_id, bool _is_hole)
+ : edge_id(_edge_id), is_hole(_is_hole) {
+ }
+ bool operator==(PolygonDegeneracy y) const {
+ return edge_id == y.edge_id && is_hole == y.is_hole;
+ }
+ bool operator<(PolygonDegeneracy y) const {
+ return edge_id < y.edge_id || (edge_id == y.edge_id && is_hole < y.is_hole);
+ }
+};
+
+// Given a graph representing a polygon, finds all degenerate edges and
+// sibling pairs and classifies them as being either shells or holes. The
+// result vector is sorted by edge id.
+//
+// REQUIRES: g.options().edge_type() == DIRECTED
+// REQUIRES: g.options().sibling_pairs() == DISCARD_EXCESS (or DISCARD)
+// REQUIRES: g.options().degenerate_edges() == DISCARD_EXCESS (or DISCARD)
+//
+// Usually callers will want to specify SiblingPairs::DISCARD_EXCESS and
+// DegenerateEdges::DISCARD_EXCESS in order to remove all redundant
+// degeneracies. DISCARD is also allowed in case you want to keep only one
+// type of degeneracy (i.e., degenerate edges or sibling pairs).
+//
+// If the graph edges cannot be assembled into loops, the result is undefined.
+// (An error may or may not be returned.)
+std::vector<PolygonDegeneracy> FindPolygonDegeneracies(
+ const S2Builder::Graph& g, S2Error* error);
+
+// Given a graph representing a polygon, returns true the graph consists
+// entirely of degenerate edges and/or sibling pairs. Such a graph represents
+// either the empty polygon together with a collection of degenerate shells,
+// or the full polygon together with a collection of degenerate holes.
+//
+// REQUIRES: g.options().edge_type() == DIRECTED
+// REQUIRES: g.options().sibling_pairs() == DISCARD_EXCESS (or DISCARD)
+// REQUIRES: g.options().degenerate_edges() == DISCARD_EXCESS (or DISCARD)
+bool IsFullyDegenerate(const S2Builder::Graph& g);
+
+} // namespace s2builderutil
+
+#endif // S2_S2BUILDERUTIL_FIND_POLYGON_DEGENERACIES_H_
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2BUILDERUTIL_GRAPH_SHAPE_H_
+#define S2_S2BUILDERUTIL_GRAPH_SHAPE_H_
+
+#include <vector>
+
+#include "s2/s2builder_graph.h"
+
+namespace s2builderutil {
+
+// An S2Shape representing the edges in an S2Builder::Graph.
+class GraphShape final : public S2Shape {
+ public:
+ using Graph = S2Builder::Graph;
+ explicit GraphShape(const Graph* g) : g_(*g) {}
+ int num_edges() const override { return g_.num_edges(); }
+ Edge edge(int e) const override {
+ Graph::Edge g_edge = g_.edge(e);
+ return Edge(g_.vertex(g_edge.first), g_.vertex(g_edge.second));
+ }
+ int dimension() const override { return 1; }
+ ReferencePoint GetReferencePoint() const override {
+ return ReferencePoint::Contained(false);
+ }
+ int num_chains() const override { return g_.num_edges(); }
+ Chain chain(int i) const override { return Chain(i, 1); }
+ Edge chain_edge(int i, int j) const override {
+ S2_DCHECK_EQ(j, 0);
+ return edge(i);
+ }
+ ChainPosition chain_position(int e) const override {
+ return ChainPosition(e, 0);
+ }
+
+ private:
+ const Graph& g_;
+};
+
+} // namespace s2builderutil
+
+#endif // S2_S2BUILDERUTIL_GRAPH_SHAPE_H_
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// Note that there are two supported output types for polygons: S2Polygon and
+// S2LaxPolygonShape. Use S2Polygon if you need the full range of operations
+// that S2Polygon implements. Use S2LaxPolygonShape if you want to represent
+// polygons with zero-area degenerate regions, or if you need a type that has
+// low memory overhead and fast initialization. However, be aware that to
+// convert from S2LaxPolygonShape to S2Polygon you will need to use S2Builder
+// again.
+//
+// Similarly, there are two supported output formats for polygon meshes:
+// S2PolygonMesh and S2LaxPolygonShapeVector. Use S2PolygonMesh if you need
+// to be able to determine which polygons are adjacent to each edge or vertex;
+// otherwise use S2LaxPolygonShapeVector, which uses less memory and is faster
+// to construct.
+
+#ifndef S2_S2BUILDERUTIL_LAX_POLYGON_LAYER_H_
+#define S2_S2BUILDERUTIL_LAX_POLYGON_LAYER_H_
+
+#include <memory>
+#include <vector>
+#include "s2/base/logging.h"
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/id_set_lexicon.h"
+#include "s2/mutable_s2shape_index.h"
+#include "s2/s2builder.h"
+#include "s2/s2builder_graph.h"
+#include "s2/s2builder_layer.h"
+#include "s2/s2error.h"
+#include "s2/s2lax_polygon_shape.h"
+
+namespace s2builderutil {
+
+// A layer type that assembles edges (directed or undirected) into an
+// S2LaxPolygonShape. Returns an error if the edges cannot be assembled into
+// loops.
+//
+// If the input edges are directed, they must be oriented such that the
+// polygon interior is to the left of all edges. Directed edges are always
+// preferred (see S2Builder::EdgeType).
+//
+// LaxPolygonLayer is implemented such that if the input to S2Builder is a
+// polygon and is not modified, then the output has the same cyclic ordering
+// of loop vertices and the same loop ordering as the input polygon.
+//
+// If the given edge graph is degenerate (i.e., it consists entirely of
+// degenerate edges and sibling pairs), then the IsFullPolygonPredicate
+// associated with the edge graph is called to determine whether the output
+// polygon should be empty (possibly with degenerate shells) or full (possibly
+// with degenerate holes). This predicate can be specified as part of the
+// S2Builder input geometry.
+class LaxPolygonLayer : public S2Builder::Layer {
+ public:
+ class Options {
+ public:
+ // Constructor that uses the default options (listed below).
+ Options();
+
+ // Constructor that specifies the edge type.
+ explicit Options(S2Builder::EdgeType edge_type);
+
+ // Indicates whether the input edges provided to S2Builder are directed or
+ // undirected. Directed edges should be used whenever possible (see
+ // S2Builder::EdgeType for details).
+ //
+ // If the input edges are directed, they should be oriented so that the
+ // polygon interior is to the left of all edges. This means that for a
+ // polygon with holes, the outer loops ("shells") should be directed
+ // counter-clockwise while the inner loops ("holes") should be directed
+ // clockwise. Note that S2Builder::AddPolygon() does this automatically.
+ //
+ // DEFAULT: S2Builder::EdgeType::DIRECTED
+ S2Builder::EdgeType edge_type() const;
+ void set_edge_type(S2Builder::EdgeType edge_type);
+
+ // Specifies whether degenerate boundaries should be discarded or kept.
+ // (A degenerate boundary consists of either a sibling edge pair or an
+ // edge from a vertex to itself.) Optionally, degenerate boundaries may
+ // be kept only if they represent shells, or only if they represent holes.
+ //
+ // This option is useful for normalizing polygons with various boundary
+ // conditions. For example, DISCARD_HOLES can be used to normalize closed
+ // polygons (those that include their boundary), since degenerate holes do
+ // not affect the set of points contained by such polygons. Similarly,
+ // DISCARD_SHELLS can be used to normalize polygons with open boundaries.
+ // DISCARD is used to normalize polygons with semi-open boundaries (since
+ // degenerate loops do not affect point containment in that case), and
+ // finally KEEP is useful for working with any type of polygon where
+ // degeneracies are assumed to contain an infinitesmal interior. (This
+ // last model is the most useful for working with simplified geometry,
+ // since it maintains the closest fidelity to the original geometry.)
+ //
+ // DEFAULT: DegenerateBoundaries::KEEP
+ enum class DegenerateBoundaries {
+ DISCARD, DISCARD_HOLES, DISCARD_SHELLS, KEEP
+ };
+ DegenerateBoundaries degenerate_boundaries() const;
+ void set_degenerate_boundaries(DegenerateBoundaries degenerate_boundaries);
+
+ private:
+ S2Builder::EdgeType edge_type_;
+ DegenerateBoundaries degenerate_boundaries_;
+ };
+
+ // Specifies that a polygon should be constructed using the given options.
+ explicit LaxPolygonLayer(S2LaxPolygonShape* polygon,
+ const Options& options = Options());
+
+ // Specifies that a polygon should be constructed using the given options,
+ // and that any labels attached to the input edges should be returned in
+ // "label_set_ids" and "label_set_lexicion".
+ //
+ // The labels associated with the edge "polygon.chain_edge(i, j)"
+ // can be retrieved as follows:
+ //
+ // for (int32 label : label_set_lexicon.id_set(label_set_ids[i][j])) {...}
+ using LabelSetIds = std::vector<std::vector<LabelSetId>>;
+ LaxPolygonLayer(S2LaxPolygonShape* polygon, LabelSetIds* label_set_ids,
+ IdSetLexicon* label_set_lexicon,
+ const Options& options = Options());
+
+ // Layer interface:
+ GraphOptions graph_options() const override;
+ void Build(const Graph& g, S2Error* error) override;
+
+ private:
+ void Init(S2LaxPolygonShape* polygon, LabelSetIds* label_set_ids,
+ IdSetLexicon* label_set_lexicon, const Options& options);
+ void AppendPolygonLoops(const Graph& g,
+ const std::vector<Graph::EdgeLoop>& edge_loops,
+ std::vector<std::vector<S2Point>>* loops) const;
+ void AppendEdgeLabels(const Graph& g,
+ const std::vector<Graph::EdgeLoop>& edge_loops);
+ void BuildDirected(Graph g, S2Error* error);
+
+ S2LaxPolygonShape* polygon_;
+ LabelSetIds* label_set_ids_;
+ IdSetLexicon* label_set_lexicon_;
+ Options options_;
+};
+
+// Like LaxPolygonLayer, but adds the polygon to a MutableS2ShapeIndex (if the
+// polygon is non-empty).
+class IndexedLaxPolygonLayer : public S2Builder::Layer {
+ public:
+ using Options = LaxPolygonLayer::Options;
+ explicit IndexedLaxPolygonLayer(MutableS2ShapeIndex* index,
+ const Options& options = Options())
+ : index_(index), polygon_(new S2LaxPolygonShape),
+ layer_(polygon_.get(), options) {}
+
+ GraphOptions graph_options() const override {
+ return layer_.graph_options();
+ }
+
+ void Build(const Graph& g, S2Error* error) override {
+ layer_.Build(g, error);
+ if (error->ok() && !polygon_->is_empty()) {
+ index_->Add(std::move(polygon_));
+ }
+ }
+
+ private:
+ MutableS2ShapeIndex* index_;
+ std::unique_ptr<S2LaxPolygonShape> polygon_;
+ LaxPolygonLayer layer_;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline LaxPolygonLayer::Options::Options()
+ : Options(S2Builder::EdgeType::DIRECTED) {
+}
+
+inline LaxPolygonLayer::Options::Options(S2Builder::EdgeType edge_type)
+ : edge_type_(edge_type),
+ degenerate_boundaries_(DegenerateBoundaries::KEEP) {
+}
+
+inline S2Builder::EdgeType LaxPolygonLayer::Options::edge_type() const {
+ return edge_type_;
+}
+
+inline void LaxPolygonLayer::Options::set_edge_type(
+ S2Builder::EdgeType edge_type) {
+ edge_type_ = edge_type;
+}
+
+inline LaxPolygonLayer::Options::DegenerateBoundaries
+LaxPolygonLayer::Options::degenerate_boundaries() const {
+ return degenerate_boundaries_;
+}
+
+inline void LaxPolygonLayer::Options::set_degenerate_boundaries(
+ DegenerateBoundaries degenerate_boundaries) {
+ degenerate_boundaries_ = degenerate_boundaries;
+}
+
+} // namespace s2builderutil
+
+#endif // S2_S2BUILDERUTIL_LAX_POLYGON_LAYER_H_
--- /dev/null
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2BUILDERUTIL_S2POINT_VECTOR_LAYER_H_
+#define S2_S2BUILDERUTIL_S2POINT_VECTOR_LAYER_H_
+
+#include <memory>
+#include <vector>
+#include "s2/base/logging.h"
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/id_set_lexicon.h"
+#include "s2/mutable_s2shape_index.h"
+#include "s2/s2builder.h"
+#include "s2/s2builder_graph.h"
+#include "s2/s2builder_layer.h"
+#include "s2/s2error.h"
+#include "s2/s2point_vector_shape.h"
+
+namespace s2builderutil {
+
+// A layer type that collects degenerate edges as points.
+// This layer expects all edges to be degenerate. In case of finding
+// non-degenerate edges it sets S2Error but it still generates the
+// output with degenerate edges.
+class S2PointVectorLayer : public S2Builder::Layer {
+ public:
+ class Options {
+ public:
+ using DuplicateEdges = GraphOptions::DuplicateEdges;
+ Options();
+ explicit Options(DuplicateEdges duplicate_edges);
+
+ // DEFAULT: DuplicateEdges::MERGE
+ DuplicateEdges duplicate_edges() const;
+ void set_duplicate_edges(DuplicateEdges duplicate_edges);
+
+ private:
+ DuplicateEdges duplicate_edges_;
+ };
+
+ explicit S2PointVectorLayer(std::vector<S2Point>* points,
+ const Options& options = Options());
+
+ using LabelSetIds = std::vector<LabelSetId>;
+ S2PointVectorLayer(std::vector<S2Point>* points, LabelSetIds* label_set_ids,
+ IdSetLexicon* label_set_lexicon,
+ const Options& options = Options());
+
+ // Layer interface:
+ GraphOptions graph_options() const override;
+ void Build(const Graph& g, S2Error* error) override;
+
+ private:
+ std::vector<S2Point>* points_;
+ LabelSetIds* label_set_ids_;
+ IdSetLexicon* label_set_lexicon_;
+ Options options_;
+};
+
+// Like S2PointVectorLayer, but adds the points to a MutableS2ShapeIndex (if
+// the point vector is non-empty).
+class IndexedS2PointVectorLayer : public S2Builder::Layer {
+ public:
+ using Options = S2PointVectorLayer::Options;
+ explicit IndexedS2PointVectorLayer(MutableS2ShapeIndex* index,
+ const Options& options = Options())
+ : index_(index), layer_(&points_, options) {}
+
+ GraphOptions graph_options() const override {
+ return layer_.graph_options();
+ }
+
+ void Build(const Graph& g, S2Error* error) override {
+ layer_.Build(g, error);
+ if (error->ok() && !points_.empty()) {
+ index_->Add(absl::make_unique<S2PointVectorShape>(std::move(points_)));
+ }
+ }
+
+ private:
+ MutableS2ShapeIndex* index_;
+ std::vector<S2Point> points_;
+ S2PointVectorLayer layer_;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline S2PointVectorLayer::Options::Options()
+ : duplicate_edges_(DuplicateEdges::MERGE) {}
+
+inline S2PointVectorLayer::Options::Options(DuplicateEdges duplicate_edges)
+ : duplicate_edges_(duplicate_edges) {}
+
+inline S2Builder::GraphOptions::DuplicateEdges
+S2PointVectorLayer::Options::duplicate_edges() const {
+ return duplicate_edges_;
+}
+
+inline void S2PointVectorLayer::Options::set_duplicate_edges(
+ S2Builder::GraphOptions::DuplicateEdges duplicate_edges) {
+ duplicate_edges_ = duplicate_edges;
+}
+
+} // namespace s2builderutil
+
+#endif // S2_S2BUILDERUTIL_S2POINT_VECTOR_LAYER_H_
--- /dev/null
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// Note that there are two supported output types for polygons: S2Polygon and
+// S2LaxPolygonShape. Use S2Polygon if you need the full range of operations
+// that S2Polygon implements. Use S2LaxPolygonShape if you want to represent
+// polygons with zero-area degenerate regions, or if you need a type that has
+// low memory overhead and fast initialization. However, be aware that to
+// convert from a S2LaxPolygonShape to an S2Polygon you will need to use
+// S2Builder again.
+//
+// Similarly, there are two supported output formats for polygon meshes:
+// S2LaxPolygonShapeVector and S2PolygonMesh. Use S2PolygonMesh if you need
+// to be able to determine which polygons are adjacent to each edge or vertex;
+// otherwise use S2LaxPolygonShapeVector, which uses less memory and is faster
+// to construct.
+
+#ifndef S2_S2BUILDERUTIL_S2POLYGON_LAYER_H_
+#define S2_S2BUILDERUTIL_S2POLYGON_LAYER_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+#include "s2/base/logging.h"
+#include "s2/util/gtl/btree_map.h"
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/id_set_lexicon.h"
+#include "s2/mutable_s2shape_index.h"
+#include "s2/s2builder.h"
+#include "s2/s2builder_graph.h"
+#include "s2/s2builder_layer.h"
+#include "s2/s2error.h"
+#include "s2/s2loop.h"
+#include "s2/s2polygon.h"
+
+namespace s2builderutil {
+
+// A layer type that assembles edges (directed or undirected) into an
+// S2Polygon. Returns an error if the edges cannot be assembled into loops.
+//
+// If the input edges are directed, they must be oriented such that the
+// polygon interior is to the left of all edges. Directed edges are always
+// preferred (see S2Builder::EdgeType).
+//
+// Before the edges are assembled into loops, "sibling pairs" consisting of an
+// edge and its reverse edge are automatically removed. Such edge pairs
+// represent zero-area degenerate regions, which S2Polygon does not allow.
+// (If you need to build polygons with degeneracies, use LaxPolygonLayer
+// instead.)
+//
+// S2PolygonLayer is implemented such that if the input to S2Builder is a
+// polygon and is not modified, then the output has the same cyclic ordering
+// of loop vertices and the same loop ordering as the input polygon.
+//
+// If the polygon has no edges, then the graph's IsFullPolygonPredicate is
+// called to determine whether the output polygon should be empty (containing
+// no points) or full (containing all points). This predicate can be
+// specified as part of the S2Builder input geometry.
+class S2PolygonLayer : public S2Builder::Layer {
+ public:
+ class Options {
+ public:
+ // Constructor that uses the default options (listed below).
+ Options();
+
+ // Constructor that specifies the edge type.
+ explicit Options(S2Builder::EdgeType edge_type);
+
+ // Indicates whether the input edges provided to S2Builder are directed or
+ // undirected. Directed edges should be used whenever possible (see
+ // S2Builder::EdgeType for details).
+ //
+ // If the input edges are directed, they should be oriented so that the
+ // polygon interior is to the left of all edges. This means that for a
+ // polygon with holes, the outer loops ("shells") should be directed
+ // counter-clockwise while the inner loops ("holes") should be directed
+ // clockwise. Note that S2Builder::AddPolygon() does this automatically.
+ //
+ // DEFAULT: S2Builder::EdgeType::DIRECTED
+ S2Builder::EdgeType edge_type() const;
+ void set_edge_type(S2Builder::EdgeType edge_type);
+
+ // If true, calls FindValidationError() on the output polygon. If any
+ // error is found, it will be returned by S2Builder::Build().
+ //
+ // Note that this option calls set_s2debug_override(S2Debug::DISABLE) in
+ // order to turn off the default error checking in debug builds.
+ //
+ // DEFAULT: false
+ bool validate() const;
+ void set_validate(bool validate);
+
+ private:
+ S2Builder::EdgeType edge_type_;
+ bool validate_;
+ };
+
+ // Specifies that a polygon should be constructed using the given options.
+ explicit S2PolygonLayer(S2Polygon* polygon,
+ const Options& options = Options());
+
+ // Specifies that a polygon should be constructed using the given options,
+ // and that any labels attached to the input edges should be returned in
+ // "label_set_ids" and "label_set_lexicion".
+ //
+ // The labels associated with the edge "polygon.loop(i).vertex({j, j+1})"
+ // can be retrieved as follows:
+ //
+ // for (int32 label : label_set_lexicon.id_set(label_set_ids[i][j])) {...}
+ using LabelSetIds = std::vector<std::vector<LabelSetId>>;
+ S2PolygonLayer(S2Polygon* polygon, LabelSetIds* label_set_ids,
+ IdSetLexicon* label_set_lexicon,
+ const Options& options = Options());
+
+ // Layer interface:
+ GraphOptions graph_options() const override;
+ void Build(const Graph& g, S2Error* error) override;
+
+ private:
+ void Init(S2Polygon* polygon, LabelSetIds* label_set_ids,
+ IdSetLexicon* label_set_lexicon, const Options& options);
+ void AppendS2Loops(const Graph& g,
+ const std::vector<Graph::EdgeLoop>& edge_loops,
+ std::vector<std::unique_ptr<S2Loop>>* loops) const;
+ void AppendEdgeLabels(const Graph& g,
+ const std::vector<Graph::EdgeLoop>& edge_loops);
+ using LoopMap = gtl::btree_map<S2Loop*, std::pair<int, bool>>;
+ void InitLoopMap(const std::vector<std::unique_ptr<S2Loop>>& loops,
+ LoopMap* loop_map) const;
+ void ReorderEdgeLabels(const LoopMap& loop_map);
+
+ S2Polygon* polygon_;
+ LabelSetIds* label_set_ids_;
+ IdSetLexicon* label_set_lexicon_;
+ Options options_;
+};
+
+// Like S2PolygonLayer, but adds the polygon to a MutableS2ShapeIndex (if the
+// polygon is non-empty).
+class IndexedS2PolygonLayer : public S2Builder::Layer {
+ public:
+ using Options = S2PolygonLayer::Options;
+ explicit IndexedS2PolygonLayer(MutableS2ShapeIndex* index,
+ const Options& options = Options())
+ : index_(index), polygon_(new S2Polygon),
+ layer_(polygon_.get(), options) {}
+
+ GraphOptions graph_options() const override {
+ return layer_.graph_options();
+ }
+
+ void Build(const Graph& g, S2Error* error) override {
+ layer_.Build(g, error);
+ if (error->ok() && !polygon_->is_empty()) {
+ index_->Add(
+ absl::make_unique<S2Polygon::OwningShape>(std::move(polygon_)));
+ }
+ }
+
+ private:
+ MutableS2ShapeIndex* index_;
+ std::unique_ptr<S2Polygon> polygon_;
+ S2PolygonLayer layer_;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline S2PolygonLayer::Options::Options()
+ : edge_type_(S2Builder::EdgeType::DIRECTED), validate_(false) {
+}
+
+inline S2PolygonLayer::Options::Options(S2Builder::EdgeType edge_type)
+ : edge_type_(edge_type), validate_(false) {
+}
+
+inline S2Builder::EdgeType S2PolygonLayer::Options::edge_type() const {
+ return edge_type_;
+}
+
+inline void S2PolygonLayer::Options::set_edge_type(
+ S2Builder::EdgeType edge_type) {
+ edge_type_ = edge_type;
+}
+
+inline bool S2PolygonLayer::Options::validate() const {
+ return validate_;
+}
+
+inline void S2PolygonLayer::Options::set_validate(bool validate) {
+ validate_ = validate;
+}
+
+} // namespace s2builderutil
+
+#endif // S2_S2BUILDERUTIL_S2POLYGON_LAYER_H_
--- /dev/null
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2BUILDERUTIL_S2POLYLINE_LAYER_H_
+#define S2_S2BUILDERUTIL_S2POLYLINE_LAYER_H_
+
+#include <memory>
+#include <vector>
+#include "s2/base/logging.h"
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/id_set_lexicon.h"
+#include "s2/mutable_s2shape_index.h"
+#include "s2/s2builder.h"
+#include "s2/s2builder_graph.h"
+#include "s2/s2builder_layer.h"
+#include "s2/s2error.h"
+#include "s2/s2polyline.h"
+
+namespace s2builderutil {
+
+// A layer type that assembles edges (directed or undirected) into an
+// S2Polyline. Returns an error if the edges cannot be assembled into a
+// single unbroken polyline.
+//
+// Duplicate edges are handled correctly (e.g., if a polyline backtracks on
+// itself, or loops around and retraces some of its previous edges.) The
+// implementation attempts to preserve the order of directed input edges
+// whenever possible, so that if the input is a polyline and it is not
+// modified by S2Builder, then the output will be the same polyline (even if
+// the polyline backtracks on itself or forms a loop). With undirected edges,
+// there are no such guarantees; for example, even if the input consists of a
+// single undirected edge, then either directed edge may be returned.
+//
+// S2PolylineLayer does not support options such as discarding sibling pairs
+// or merging duplicate edges because these options can split the polyline
+// into several pieces. Use S2PolylineVectorLayer if you need these features.
+class S2PolylineLayer : public S2Builder::Layer {
+ public:
+ class Options {
+ public:
+ // Constructor that uses the default options (listed below).
+ Options();
+
+ // Constructor that specifies the edge type.
+ explicit Options(S2Builder::EdgeType edge_type);
+
+ // Indicates whether the input edges provided to S2Builder are directed or
+ // undirected. Directed edges should be used whenever possible to avoid
+ // ambiguity.
+ //
+ // DEFAULT: S2Builder::EdgeType::DIRECTED
+ S2Builder::EdgeType edge_type() const;
+ void set_edge_type(S2Builder::EdgeType edge_type);
+
+ // If true, calls FindValidationError() on the output polyline. If any
+ // error is found, it will be returned by S2Builder::Build().
+ //
+ // Note that this option calls set_s2debug_override(S2Debug::DISABLE) in
+ // order to turn off the default error checking in debug builds.
+ //
+ // DEFAULT: false
+ bool validate() const;
+ void set_validate(bool validate);
+
+ private:
+ S2Builder::EdgeType edge_type_;
+ bool validate_;
+ };
+
+ // Specifies that a polyline should be constructed using the given options.
+ explicit S2PolylineLayer(S2Polyline* polyline,
+ const Options& options = Options());
+
+ // Specifies that a polyline should be constructed using the given options,
+ // and that any labels attached to the input edges should be returned in
+ // "label_set_ids" and "label_set_lexicion".
+ //
+ // The labels associated with the edge "polyline.vertex({j, j+1})" can be
+ // retrieved as follows:
+ //
+ // for (int32 label : label_set_lexicon.id_set(label_set_ids[j])) {...}
+ using LabelSetIds = std::vector<LabelSetId>;
+ S2PolylineLayer(S2Polyline* polyline, LabelSetIds* label_set_ids,
+ IdSetLexicon* label_set_lexicon,
+ const Options& options = Options());
+
+ // Layer interface:
+ GraphOptions graph_options() const override;
+ void Build(const Graph& g, S2Error* error) override;
+
+ private:
+ void Init(S2Polyline* polyline, LabelSetIds* label_set_ids,
+ IdSetLexicon* label_set_lexicon, const Options& options);
+
+ S2Polyline* polyline_;
+ LabelSetIds* label_set_ids_;
+ IdSetLexicon* label_set_lexicon_;
+ Options options_;
+};
+
+// Like S2PolylineLayer, but adds the polyline to a MutableS2ShapeIndex (if the
+// polyline is non-empty).
+class IndexedS2PolylineLayer : public S2Builder::Layer {
+ public:
+ using Options = S2PolylineLayer::Options;
+ explicit IndexedS2PolylineLayer(MutableS2ShapeIndex* index,
+ const Options& options = Options())
+ : index_(index), polyline_(new S2Polyline),
+ layer_(polyline_.get(), options) {}
+
+ GraphOptions graph_options() const override {
+ return layer_.graph_options();
+ }
+
+ void Build(const Graph& g, S2Error* error) override {
+ layer_.Build(g, error);
+ if (error->ok() && polyline_->num_vertices() > 0) {
+ index_->Add(
+ absl::make_unique<S2Polyline::OwningShape>(std::move(polyline_)));
+ }
+ }
+
+ private:
+ MutableS2ShapeIndex* index_;
+ std::unique_ptr<S2Polyline> polyline_;
+ S2PolylineLayer layer_;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline S2PolylineLayer::Options::Options()
+ : edge_type_(S2Builder::EdgeType::DIRECTED), validate_(false) {
+}
+
+inline S2PolylineLayer::Options::Options(S2Builder::EdgeType edge_type)
+ : edge_type_(edge_type), validate_(false) {
+}
+
+inline S2Builder::EdgeType S2PolylineLayer::Options::edge_type() const {
+ return edge_type_;
+}
+
+inline void S2PolylineLayer::Options::set_edge_type(
+ S2Builder::EdgeType edge_type) {
+ edge_type_ = edge_type;
+}
+
+inline bool S2PolylineLayer::Options::validate() const {
+ return validate_;
+}
+
+inline void S2PolylineLayer::Options::set_validate(bool validate) {
+ validate_ = validate;
+}
+
+} // namespace s2builderutil
+
+#endif // S2_S2BUILDERUTIL_S2POLYLINE_LAYER_H_
--- /dev/null
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2BUILDERUTIL_S2POLYLINE_VECTOR_LAYER_H_
+#define S2_S2BUILDERUTIL_S2POLYLINE_VECTOR_LAYER_H_
+
+#include <memory>
+#include <vector>
+#include "s2/base/logging.h"
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/id_set_lexicon.h"
+#include "s2/mutable_s2shape_index.h"
+#include "s2/s2builder.h"
+#include "s2/s2builder_graph.h"
+#include "s2/s2builder_layer.h"
+#include "s2/s2debug.h"
+#include "s2/s2error.h"
+#include "s2/s2polyline.h"
+
+namespace s2builderutil {
+
+// A layer type that assembles edges (directed or undirected) into multiple
+// S2Polylines. Returns an error if S2Builder found any problem with the
+// input edges; this layer type does not generate any errors of its own.
+//
+// Duplicate edges are handled correctly (e.g., if a polyline backtracks on
+// itself, or loops around and retraces some of its previous edges.) The
+// implementation attempts to preserve the order of the input edges whenever
+// possible, so that if the input is a polyline and it is not modified by
+// S2Builder, then the output will be the same polyline even if the polyline
+// forms a loop. However, note that this is not guaranteed when undirected
+// edges are used: for example, if the input consists of a single undirected
+// edge, then either directed edge may be returned.
+class S2PolylineVectorLayer : public S2Builder::Layer {
+ public:
+ class Options {
+ public:
+ // Constructor that uses the default options (listed below).
+ Options();
+
+ // Constructor that specifies the edge type.
+ explicit Options(S2Builder::EdgeType edge_type);
+
+ // Indicates whether the input edges provided to S2Builder are directed or
+ // undirected.
+ //
+ // Directed edges should be used whenever possible to avoid ambiguity.
+ // The implementation attempts to preserve the structure of directed input
+ // edges whenever possible, so that if the input is a vector of disjoint
+ // polylines and none of them need to be modified then the output will be
+ // the same polylines in the same order. With undirected edges, there are
+ // no such guarantees.
+ //
+ // DEFAULT: S2Builder::EdgeType::DIRECTED
+ S2Builder::EdgeType edge_type() const;
+ void set_edge_type(S2Builder::EdgeType edge_type);
+
+ // Indicates whether polylines should be "paths" (which don't allow
+ // duplicate vertices, except possibly the first and last vertex) or
+ // "walks" (which allow duplicate vertices and edges).
+ //
+ // If your input consists of polylines, and you want to split them into
+ // separate pieces whenever they self-intersect or cross each other, then
+ // use PolylineType::PATH (and probably use split_crossing_edges()). If
+ // you don't mind if your polylines backtrack or contain loops, then use
+ // PolylineType::WALK.
+ //
+ // DEFAULT: PolylineType::PATH
+ using PolylineType = S2Builder::Graph::PolylineType;
+ PolylineType polyline_type() const;
+ void set_polyline_type(PolylineType polyline_type);
+
+ // Indicates whether duplicate edges in the input should be kept (KEEP) or
+ // merged together (MERGE). Note you can use edge labels to determine
+ // which input edges were merged into a given output edge.
+ //
+ // DEFAULT: DuplicateEdges::KEEP
+ using DuplicateEdges = GraphOptions::DuplicateEdges;
+ DuplicateEdges duplicate_edges() const;
+ void set_duplicate_edges(DuplicateEdges duplicate_edges);
+
+ // Indicates whether sibling edge pairs (i.e., pairs consisting of an edge
+ // and its reverse edge) should be kept (KEEP) or discarded (DISCARD).
+ // For example, if a polyline backtracks on itself, the DISCARD option
+ // would cause this section of the polyline to be removed. Note that this
+ // option may cause a single polyline to split into several pieces (e.g.,
+ // if a polyline has a "lollipop" shape).
+ //
+ // REQUIRES: sibling_pairs == { DISCARD, KEEP }
+ // (the CREATE and REQUIRE options are not allowed)
+ //
+ // DEFAULT: SiblingPairs::KEEP
+ using SiblingPairs = GraphOptions::SiblingPairs;
+ SiblingPairs sibling_pairs() const;
+ void set_sibling_pairs(SiblingPairs sibling_pairs);
+
+ // If true, calls FindValidationError() on each output polyline. If any
+ // error is found, it will be returned by S2Builder::Build().
+ //
+ // Note that this option calls set_s2debug_override(S2Debug::DISABLE) in
+ // order to turn off the default error checking in debug builds.
+ //
+ // DEFAULT: false
+ bool validate() const;
+ void set_validate(bool validate);
+
+ // This method can turn off the automatic validity checks triggered by the
+ // --s2debug flag (which is on by default in debug builds). The main
+ // reason to do this is if your code already does its own error checking,
+ // or if you need to work with invalid geometry for some reason.
+ //
+ // In any case, polylines have very few restrictions so they are unlikely
+ // to have errors. Errors include vertices that aren't unit length (which
+ // can only happen if they are present in the input data), or adjacent
+ // vertices that are at antipodal points on the sphere (unlikely with real
+ // data). The other possible error is adjacent identical vertices, but
+ // this can't happen because S2Builder does not generate such polylines.
+ //
+ // DEFAULT: S2Debug::ALLOW
+ S2Debug s2debug_override() const;
+ void set_s2debug_override(S2Debug override);
+
+ private:
+ S2Builder::EdgeType edge_type_;
+ PolylineType polyline_type_;
+ DuplicateEdges duplicate_edges_;
+ SiblingPairs sibling_pairs_;
+ bool validate_;
+ S2Debug s2debug_override_;
+ };
+
+ // Specifies that a vector of polylines should be constructed using the
+ // given options.
+ explicit S2PolylineVectorLayer(
+ std::vector<std::unique_ptr<S2Polyline>>* polylines,
+ const Options& options = Options());
+
+ // Specifies that a vector of polylines should be constructed using the
+ // given options, and that any labels attached to the input edges should be
+ // returned in "label_set_ids" and "label_set_lexicion".
+ //
+ // The labels associated with the edge "polyline[i].vertex({j, j+1})" can be
+ // retrieved as follows:
+ //
+ // for (int32 label : label_set_lexicon.id_set(label_set_ids[i][j])) {...}
+ using LabelSetIds = std::vector<std::vector<LabelSetId>>;
+ S2PolylineVectorLayer(std::vector<std::unique_ptr<S2Polyline>>* polylines,
+ LabelSetIds* label_set_ids,
+ IdSetLexicon* label_set_lexicon,
+ const Options& options = Options());
+
+ // Layer interface:
+ GraphOptions graph_options() const override;
+ void Build(const Graph& g, S2Error* error) override;
+
+ private:
+ void Init(std::vector<std::unique_ptr<S2Polyline>>* polylines,
+ LabelSetIds* label_set_ids, IdSetLexicon* label_set_lexicon,
+ const Options& options);
+
+ std::vector<std::unique_ptr<S2Polyline>>* polylines_;
+ LabelSetIds* label_set_ids_;
+ IdSetLexicon* label_set_lexicon_;
+ Options options_;
+};
+
+// Like S2PolylineVectorLayer, but adds the polylines to a MutableS2ShapeIndex.
+class IndexedS2PolylineVectorLayer : public S2Builder::Layer {
+ public:
+ using Options = S2PolylineVectorLayer::Options;
+ explicit IndexedS2PolylineVectorLayer(MutableS2ShapeIndex* index,
+ const Options& options = Options())
+ : index_(index), layer_(&polylines_, options) {}
+
+ GraphOptions graph_options() const override {
+ return layer_.graph_options();
+ }
+
+ void Build(const Graph& g, S2Error* error) override {
+ layer_.Build(g, error);
+ if (error->ok()) {
+ for (auto& polyline : polylines_) {
+ index_->Add(
+ absl::make_unique<S2Polyline::OwningShape>(std::move(polyline)));
+ }
+ }
+ }
+
+ private:
+ MutableS2ShapeIndex* index_;
+ std::vector<std::unique_ptr<S2Polyline>> polylines_;
+ S2PolylineVectorLayer layer_;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline S2PolylineVectorLayer::Options::Options()
+ : edge_type_(S2Builder::EdgeType::DIRECTED),
+ polyline_type_(PolylineType::PATH),
+ duplicate_edges_(DuplicateEdges::KEEP),
+ sibling_pairs_(SiblingPairs::KEEP),
+ validate_(false),
+ s2debug_override_(S2Debug::ALLOW) {
+}
+
+inline S2PolylineVectorLayer::Options::Options(S2Builder::EdgeType edge_type)
+ : edge_type_(edge_type),
+ polyline_type_(PolylineType::PATH),
+ duplicate_edges_(DuplicateEdges::KEEP),
+ sibling_pairs_(SiblingPairs::KEEP),
+ validate_(false),
+ s2debug_override_(S2Debug::ALLOW) {
+}
+
+inline S2Builder::EdgeType S2PolylineVectorLayer::Options::edge_type() const {
+ return edge_type_;
+}
+
+inline void S2PolylineVectorLayer::Options::set_edge_type(
+ S2Builder::EdgeType edge_type) {
+ edge_type_ = edge_type;
+}
+
+inline S2PolylineVectorLayer::Options::PolylineType
+S2PolylineVectorLayer::Options::polyline_type() const {
+ return polyline_type_;
+}
+
+inline void S2PolylineVectorLayer::Options::set_polyline_type(
+ PolylineType polyline_type) {
+ polyline_type_ = polyline_type;
+}
+
+inline S2PolylineVectorLayer::Options::DuplicateEdges
+S2PolylineVectorLayer::Options::duplicate_edges() const {
+ return duplicate_edges_;
+}
+
+inline void S2PolylineVectorLayer::Options::set_duplicate_edges(
+ DuplicateEdges duplicate_edges) {
+ duplicate_edges_ = duplicate_edges;
+}
+
+inline S2PolylineVectorLayer::Options::SiblingPairs
+S2PolylineVectorLayer::Options::sibling_pairs() const {
+ return sibling_pairs_;
+}
+
+inline void S2PolylineVectorLayer::Options::set_sibling_pairs(
+ SiblingPairs sibling_pairs) {
+ S2_DCHECK(sibling_pairs == SiblingPairs::KEEP ||
+ sibling_pairs == SiblingPairs::DISCARD);
+ sibling_pairs_ = sibling_pairs;
+}
+
+inline bool S2PolylineVectorLayer::Options::validate() const {
+ return validate_;
+}
+
+inline void S2PolylineVectorLayer::Options::set_validate(bool validate) {
+ validate_ = validate;
+ set_s2debug_override(S2Debug::DISABLE);
+}
+
+inline S2Debug S2PolylineVectorLayer::Options::s2debug_override() const {
+ return s2debug_override_;
+}
+
+inline void S2PolylineVectorLayer::Options::set_s2debug_override(
+ S2Debug s2debug_override) {
+ s2debug_override_ = s2debug_override;
+}
+
+} // namespace s2builderutil
+
+#endif // S2_S2BUILDERUTIL_S2POLYLINE_VECTOR_LAYER_H_
--- /dev/null
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2BUILDERUTIL_SNAP_FUNCTIONS_H_
+#define S2_S2BUILDERUTIL_SNAP_FUNCTIONS_H_
+
+#include <memory>
+#include "s2/s1angle.h"
+#include "s2/s2builder.h"
+#include "s2/s2cell_id.h"
+
+namespace s2builderutil {
+
+// A SnapFunction that snaps every vertex to itself. It should be used when
+// vertices do not need to be snapped to a discrete set of locations (such as
+// E7 lat/lngs), or when maximum accuracy is desired.
+//
+// If the given "snap_radius" is zero, then all input vertices are preserved
+// exactly. Otherwise, S2Builder merges nearby vertices to ensure that no
+// vertex pair is closer than "snap_radius". Furthermore, vertices are
+// separated from non-incident edges by at least "min_edge_vertex_separation",
+// equal to (0.5 * snap_radius). For example, if the snap_radius is 1km, then
+// vertices will be separated from non-incident edges by at least 500m.
+class IdentitySnapFunction : public S2Builder::SnapFunction {
+ public:
+ // The default constructor uses a snap_radius of zero (i.e., no snapping).
+ IdentitySnapFunction();
+
+ // Convenience constructor that calls set_snap_radius().
+ explicit IdentitySnapFunction(S1Angle snap_radius);
+
+ // REQUIRES: snap_radius <= SnapFunction::kMaxSnapRadius()
+ void set_snap_radius(S1Angle snap_radius);
+ S1Angle snap_radius() const override;
+
+ // For the identity snap function, all vertex pairs are separated by at
+ // least snap_radius().
+ S1Angle min_vertex_separation() const override;
+
+ // For the identity snap function, edges are separated from all non-incident
+ // vertices by at least 0.5 * snap_radius().
+ S1Angle min_edge_vertex_separation() const override;
+
+ S2Point SnapPoint(const S2Point& point) const override;
+
+ std::unique_ptr<SnapFunction> Clone() const override;
+
+ private:
+ // Copying and assignment are allowed.
+ S1Angle snap_radius_;
+};
+
+// A SnapFunction that snaps vertices to S2CellId centers. This can be useful
+// if you want to encode your geometry compactly using S2Polygon::Encode(),
+// for example. You can snap to the centers of cells at any level.
+//
+// Every snap level has a corresponding minimum snap radius, which is simply
+// the maximum distance that a vertex can move when snapped. It is
+// approximately equal to half of the maximum diagonal length for cells at the
+// chosen level. You can also set the snap radius to a larger value; for
+// example, you could snap to the centers of leaf cells (1cm resolution) but
+// set the snap_radius() to 10m. This would result in significant extra
+// simplification, without moving vertices unnecessarily (i.e., vertices that
+// are at least 10m away from all other vertices will move by less than 1cm).
+class S2CellIdSnapFunction : public S2Builder::SnapFunction {
+ public:
+ // The default constructor snaps to S2CellId::kMaxLevel (i.e., the centers
+ // of leaf cells), and uses the minimum allowable snap radius at that level.
+ S2CellIdSnapFunction();
+
+ // Convenience constructor equivalent to calling set_level(level).
+ explicit S2CellIdSnapFunction(int level);
+
+ // Snaps vertices to S2Cell centers at the given level. As a side effect,
+ // this method also resets "snap_radius" to the minimum value allowed at
+ // this level:
+ //
+ // set_snap_radius(MinSnapRadiusForLevel(level))
+ //
+ // This means that if you want to use a larger snap radius than the minimum,
+ // you must call set_snap_radius() *after* calling set_level().
+ void set_level(int level);
+ int level() const;
+
+ // Defines the snap radius to be used (see s2builder.h). The snap radius
+ // must be at least the minimum value for the current level(), but larger
+ // values can also be used (e.g., to simplify the geometry).
+ //
+ // REQUIRES: snap_radius >= MinSnapRadiusForLevel(level())
+ // REQUIRES: snap_radius <= SnapFunction::kMaxSnapRadius()
+ void set_snap_radius(S1Angle snap_radius);
+ S1Angle snap_radius() const override;
+
+ // Returns the minimum allowable snap radius for the given S2Cell level
+ // (approximately equal to half of the maximum cell diagonal length).
+ static S1Angle MinSnapRadiusForLevel(int level);
+
+ // Returns the minimum S2Cell level (i.e., largest S2Cells) such that
+ // vertices will not move by more than "snap_radius". This can be useful
+ // when choosing an appropriate level to snap to. The return value is
+ // always a valid level (out of range values are silently clamped).
+ //
+ // If you want to choose the snap level based on a distance, and then use
+ // the minimum possible snap radius for the chosen level, do this:
+ //
+ // S2CellIdSnapFunction f(
+ // S2CellIdSnapFunction::LevelForMaxSnapRadius(distance));
+ static int LevelForMaxSnapRadius(S1Angle snap_radius);
+
+ // For S2CellId snapping, the minimum separation between vertices depends on
+ // level() and snap_radius(). It can vary between 0.5 * snap_radius()
+ // and snap_radius().
+ S1Angle min_vertex_separation() const override;
+
+ // For S2CellId snapping, the minimum separation between edges and
+ // non-incident vertices depends on level() and snap_radius(). It can
+ // be as low as 0.219 * snap_radius(), but is typically 0.5 * snap_radius()
+ // or more.
+ S1Angle min_edge_vertex_separation() const override;
+
+ S2Point SnapPoint(const S2Point& point) const override;
+
+ std::unique_ptr<SnapFunction> Clone() const override;
+
+ private:
+ // Copying and assignment are allowed.
+ int level_;
+ S1Angle snap_radius_;
+};
+
+// A SnapFunction that snaps vertices to S2LatLng E5, E6, or E7 coordinates.
+// These coordinates are expressed in degrees multiplied by a power of 10 and
+// then rounded to the nearest integer. For example, in E6 coordinates the
+// point (23.12345651, -45.65432149) would become (23123457, -45654321).
+//
+// The main argument of the SnapFunction is the exponent for the power of 10
+// that coordinates should be multipled by before rounding. For example,
+// IntLatLngSnapFunction(7) is a function that snaps to E7 coordinates. The
+// exponent can range from 0 to 10.
+//
+// Each exponent has a corresponding minimum snap radius, which is simply the
+// maximum distance that a vertex can move when snapped. It is approximately
+// equal to 1/sqrt(2) times the nominal point spacing; for example, for
+// snapping to E7 the minimum snap radius is (1e-7 / sqrt(2)) degrees.
+// You can also set the snap radius to any value larger than this; this can
+// result in significant extra simplification (similar to using a larger
+// exponent) but does not move vertices unnecessarily.
+class IntLatLngSnapFunction : public S2Builder::SnapFunction {
+ public:
+ // The default constructor yields an invalid snap function. You must set
+ // the exponent explicitly before using it.
+ IntLatLngSnapFunction();
+
+ // Convenience constructor equivalent to calling set_exponent(exponent).
+ explicit IntLatLngSnapFunction(int exponent);
+
+ // Snaps vertices to points whose (lat, lng) coordinates are integers after
+ // converting to degrees and multiplying by 10 raised to the given exponent.
+ // For example, (exponent == 7) yields E7 coordinates. As a side effect,
+ // this method also resets "snap_radius" to the minimum value allowed for
+ // this exponent:
+ //
+ // set_snap_radius(MinSnapRadiusForExponent(exponent))
+ //
+ // This means that if you want to use a larger snap radius than the minimum,
+ // you must call set_snap_radius() *after* calling set_exponent().
+ //
+ // REQUIRES: kMinExponent <= exponent <= kMaxExponent
+ void set_exponent(int exponent);
+ int exponent() const;
+
+ // The minum exponent supported for snapping.
+ static const int kMinExponent = 0;
+
+ // The maximum exponent supported for snapping.
+ static const int kMaxExponent = 10;
+
+ // Defines the snap radius to be used (see s2builder.h). The snap radius
+ // must be at least the minimum value for the current exponent(), but larger
+ // values can also be used (e.g., to simplify the geometry).
+ //
+ // REQUIRES: snap_radius >= MinSnapRadiusForExponent(exponent())
+ // REQUIRES: snap_radius <= SnapFunction::kMaxSnapRadius()
+ void set_snap_radius(S1Angle snap_radius);
+ S1Angle snap_radius() const override;
+
+ // Returns the minimum allowable snap radius for the given exponent
+ // (approximately equal to (pow(10, -exponent) / sqrt(2)) degrees).
+ static S1Angle MinSnapRadiusForExponent(int exponent);
+
+ // Returns the minimum exponent such that vertices will not move by more
+ // than "snap_radius". This can be useful when choosing an appropriate
+ // exponent for snapping. The return value is always a valid exponent
+ // (out of range values are silently clamped).
+ //
+ // If you want to choose the exponent based on a distance, and then use
+ // the minimum possible snap radius for that exponent, do this:
+ //
+ // IntLatLngSnapFunction f(
+ // IntLatLngSnapFunction::ExponentForMaxSnapRadius(distance));
+ static int ExponentForMaxSnapRadius(S1Angle snap_radius);
+
+ // For IntLatLng snapping, the minimum separation between vertices depends on
+ // exponent() and snap_radius(). It can vary between snap_radius()
+ // and snap_radius().
+ S1Angle min_vertex_separation() const override;
+
+ // For IntLatLng snapping, the minimum separation between edges and
+ // non-incident vertices depends on level() and snap_radius(). It can
+ // be as low as 0.222 * snap_radius(), but is typically 0.39 * snap_radius()
+ // or more.
+ S1Angle min_edge_vertex_separation() const override;
+ S2Point SnapPoint(const S2Point& point) const override;
+ std::unique_ptr<SnapFunction> Clone() const override;
+
+ private:
+ // Copying and assignment are allowed.
+ int exponent_;
+ S1Angle snap_radius_;
+ double from_degrees_, to_degrees_;
+};
+
+} // namespace s2builderutil
+
+#endif // S2_S2BUILDERUTIL_SNAP_FUNCTIONS_H_
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2BUILDERUTIL_TESTING_H_
+#define S2_S2BUILDERUTIL_TESTING_H_
+
+#include <vector>
+
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/s2builder.h"
+#include "s2/s2builder_graph.h"
+#include "s2/s2builder_layer.h"
+
+namespace s2builderutil {
+
+// A class that copies an S2Builder::Graph and owns the underlying data
+// (unlike S2Builder::Graph, which is just a view).
+class GraphClone {
+ public:
+ GraphClone() {} // Must call Init().
+ explicit GraphClone(const S2Builder::Graph& g) { Init(g); }
+ void Init(const S2Builder::Graph& g);
+ const S2Builder::Graph& graph() { return g_; }
+
+ private:
+ S2Builder::GraphOptions options_;
+ std::vector<S2Point> vertices_;
+ std::vector<S2Builder::Graph::Edge> edges_;
+ std::vector<S2Builder::Graph::InputEdgeIdSetId> input_edge_id_set_ids_;
+ IdSetLexicon input_edge_id_set_lexicon_;
+ std::vector<S2Builder::Graph::LabelSetId> label_set_ids_;
+ IdSetLexicon label_set_lexicon_;
+ S2Builder::IsFullPolygonPredicate is_full_polygon_predicate_;
+ S2Builder::Graph g_;
+};
+
+// A layer type that copies an S2Builder::Graph into a GraphClone object
+// (which owns the underlying data, unlike S2Builder::Graph itself).
+class GraphCloningLayer : public S2Builder::Layer {
+ public:
+ GraphCloningLayer(const S2Builder::GraphOptions& graph_options,
+ GraphClone* gc)
+ : graph_options_(graph_options), gc_(gc) {}
+
+ S2Builder::GraphOptions graph_options() const override {
+ return graph_options_;
+ }
+
+ void Build(const S2Builder::Graph& g, S2Error* error) override {
+ gc_->Init(g);
+ }
+
+ private:
+ GraphOptions graph_options_;
+ GraphClone* gc_;
+};
+
+// A layer type that copies an S2Builder::Graph and appends it to a vector,
+// and appends the corresponding GraphClone object (which owns the Graph data)
+// to a separate vector.
+class GraphAppendingLayer : public S2Builder::Layer {
+ public:
+ GraphAppendingLayer(
+ const S2Builder::GraphOptions& graph_options,
+ std::vector<S2Builder::Graph>* graphs,
+ std::vector<std::unique_ptr<GraphClone>>* clones)
+ : graph_options_(graph_options), graphs_(graphs), clones_(clones) {}
+
+ S2Builder::GraphOptions graph_options() const override {
+ return graph_options_;
+ }
+
+ void Build(const S2Builder::Graph& g, S2Error* error) override {
+ clones_->push_back(absl::make_unique<GraphClone>(g));
+ graphs_->push_back(clones_->back()->graph());
+ }
+
+ private:
+ GraphOptions graph_options_;
+ std::vector<S2Builder::Graph>* graphs_;
+ std::vector<std::unique_ptr<GraphClone>>* clones_;
+};
+
+} // namespace s2builderutil
+
+#endif // S2_S2BUILDERUTIL_TESTING_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2CAP_H_
+#define S2_S2CAP_H_
+
+#include <algorithm>
+#include <cmath>
+#include <iosfwd>
+#include <vector>
+
+#include "s2/base/logging.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/s1angle.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s2pointutil.h"
+#include "s2/s2region.h"
+
+class Decoder;
+class Encoder;
+class S2Cell;
+class S2LatLngRect;
+
+// S2Cap represents a disc-shaped region defined by a center and radius.
+// Technically this shape is called a "spherical cap" (rather than disc)
+// because it is not planar; the cap represents a portion of the sphere that
+// has been cut off by a plane. The boundary of the cap is the circle defined
+// by the intersection of the sphere and the plane. For containment purposes,
+// the cap is a closed set, i.e. it contains its boundary.
+//
+// For the most part, you can use a spherical cap wherever you would use a
+// disc in planar geometry. The radius of the cap is measured along the
+// surface of the sphere (rather than the straight-line distance through the
+// interior). Thus a cap of radius Pi/2 is a hemisphere, and a cap of radius
+// Pi covers the entire sphere.
+//
+// A cap can also be defined by its center point and height. The height is
+// simply the distance from the center point to the cutoff plane. There is
+// also support for empty and full caps, which contain no points and all
+// points respectively.
+//
+// This class is intended to be copied by value as desired. It uses the
+// default copy constructor and assignment operator, however it is not a
+// "plain old datatype" (POD) because it has virtual functions.
+class S2Cap final : public S2Region {
+ public:
+ // The default constructor returns an empty S2Cap.
+ S2Cap() : center_(1, 0, 0), radius_(S1ChordAngle::Negative()) {}
+
+ // Constructs a cap with the given center and radius. A negative radius
+ // yields an empty cap; a radius of 180 degrees or more yields a full cap
+ // (containing the entire sphere). "center" should be unit length.
+ S2Cap(const S2Point& center, S1Angle radius);
+
+ // Constructs a cap where the angle is expressed as an S1ChordAngle. This
+ // constructor is more efficient than the one above.
+ S2Cap(const S2Point& center, S1ChordAngle radius);
+
+ // Convenience function that creates a cap containing a single point. This
+ // method is more efficient that the S2Cap(center, radius) constructor.
+ static S2Cap FromPoint(const S2Point& center);
+
+ // Returns a cap with the given center and height (see comments above). A
+ // negative height yields an empty cap; a height of 2 or more yields a full
+ // cap. "center" should be unit length.
+ static S2Cap FromCenterHeight(const S2Point& center, double height);
+
+ // Return a cap with the given center and surface area. Note that the area
+ // can also be interpreted as the solid angle subtended by the cap (because
+ // the sphere has unit radius). A negative area yields an empty cap; an
+ // area of 4*Pi or more yields a full cap. "center" should be unit length.
+ static S2Cap FromCenterArea(const S2Point& center, double area);
+
+ // Return an empty cap, i.e. a cap that contains no points.
+ static S2Cap Empty();
+
+ // Return a full cap, i.e. a cap that contains all points.
+ static S2Cap Full();
+
+ ~S2Cap() override {}
+
+ // Accessor methods.
+ const S2Point& center() const { return center_; }
+ S1ChordAngle radius() const { return radius_; }
+
+ // Returns the height of the cap, i.e. the distance from the center point to
+ // the cutoff plane.
+ double height() const;
+
+ // Return the cap radius as an S1Angle. (Note that the cap angle is stored
+ // internally as an S1ChordAngle, so this method requires a trigonometric
+ // operation and may yield a slightly different result than the value passed
+ // to the (S2Point, S1Angle) constructor.)
+ S1Angle GetRadius() const;
+
+ // Return the area of the cap.
+ double GetArea() const;
+
+ // Return the true centroid of the cap multiplied by its surface area (see
+ // s2centroids.h for details on centroids). The result lies on the ray from
+ // the origin through the cap's center, but it is not unit length. Note that
+ // if you just want the "surface centroid", i.e. the normalized result, then
+ // it is much simpler just to call center().
+ //
+ // The reason for multiplying the result by the cap area is to make it
+ // easier to compute the centroid of more complicated shapes. The centroid
+ // of a union of disjoint regions can be computed simply by adding their
+ // GetCentroid() results. Caveat: for caps that contain a single point
+ // (i.e., zero radius), this method always returns the origin (0, 0, 0).
+ // This is because shapes with no area don't affect the centroid of a
+ // union whose total area is positive.
+ S2Point GetCentroid() const;
+
+ // We allow negative heights (to represent empty caps) but heights are
+ // normalized so that they do not exceed 2.
+ bool is_valid() const;
+
+ // Return true if the cap is empty, i.e. it contains no points.
+ bool is_empty() const;
+
+ // Return true if the cap is full, i.e. it contains all points.
+ bool is_full() const;
+
+ // Return the complement of the interior of the cap. A cap and its
+ // complement have the same boundary but do not share any interior points.
+ // The complement operator is not a bijection because the complement of a
+ // singleton cap (containing a single point) is the same as the complement
+ // of an empty cap.
+ S2Cap Complement() const;
+
+ // Return true if and only if this cap contains the given other cap
+ // (in a set containment sense, e.g. every cap contains the empty cap).
+ bool Contains(const S2Cap& other) const;
+
+ // Return true if and only if this cap intersects the given other cap,
+ // i.e. whether they have any points in common.
+ bool Intersects(const S2Cap& other) const;
+
+ // Return true if and only if the interior of this cap intersects the
+ // given other cap. (This relationship is not symmetric, since only
+ // the interior of this cap is used.)
+ bool InteriorIntersects(const S2Cap& other) const;
+
+ // Return true if and only if the given point is contained in the interior
+ // of the cap (i.e. the cap excluding its boundary). "p" should be be a
+ // unit-length vector.
+ bool InteriorContains(const S2Point& p) const;
+
+ // Increase the cap height if necessary to include the given point. If the
+ // cap is empty then the center is set to the given point, but otherwise the
+ // center is not changed. "p" should be a unit-length vector.
+ void AddPoint(const S2Point& p);
+
+ // Increase the cap height if necessary to include "other". If the current
+ // cap is empty it is set to the given other cap.
+ void AddCap(const S2Cap& other);
+
+ // Return a cap that contains all points within a given distance of this
+ // cap. Note that any expansion of the empty cap is still empty.
+ S2Cap Expanded(S1Angle distance) const;
+
+ // Return the smallest cap which encloses this cap and "other".
+ S2Cap Union(const S2Cap& other) const;
+
+ ////////////////////////////////////////////////////////////////////////
+ // S2Region interface (see s2region.h for details):
+
+ S2Cap* Clone() const override;
+ S2Cap GetCapBound() const override;
+ S2LatLngRect GetRectBound() const override;
+ void GetCellUnionBound(std::vector<S2CellId> *cell_ids) const override;
+ bool Contains(const S2Cell& cell) const override;
+ bool MayIntersect(const S2Cell& cell) const override;
+
+ // The point "p" should be a unit-length vector.
+ bool Contains(const S2Point& p) const override;
+
+ // Appends a serialized representation of the S2Cap to "encoder".
+ //
+ // REQUIRES: "encoder" uses the default constructor, so that its buffer
+ // can be enlarged as necessary by calling Ensure(int).
+ void Encode(Encoder* const encoder) const;
+
+ // Decodes an S2Cap encoded with Encode(). Returns true on success.
+ bool Decode(Decoder* const decoder);
+
+ ///////////////////////////////////////////////////////////////////////
+ // The following static methods are convenience functions for assertions
+ // and testing purposes only.
+
+ // Return true if two caps are identical.
+ bool operator==(const S2Cap& other) const;
+
+ // Return true if the cap center and height differ by at most "max_error"
+ // from the given cap "other".
+ bool ApproxEquals(const S2Cap& other,
+ S1Angle max_error = S1Angle::Radians(1e-14)) const;
+
+ private:
+ // Here are some useful relationships between the cap height (h), the cap
+ // radius (r), the maximum chord length from the cap's center (d), and the
+ // radius of cap's base (a).
+ //
+ // h = 1 - cos(r)
+ // = 2 * sin^2(r/2)
+ // d^2 = 2 * h
+ // = a^2 + h^2
+
+ // Return true if the cap intersects "cell", given that the cap does contain
+ // any of the cell vertices (supplied in "vertices", an array of length 4).
+ bool Intersects(const S2Cell& cell, const S2Point* vertices) const;
+
+ S2Point center_;
+ S1ChordAngle radius_;
+};
+
+std::ostream& operator<<(std::ostream& os, const S2Cap& cap);
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline S2Cap::S2Cap(const S2Point& center, S1Angle radius)
+ : center_(center), radius_(std::min(radius, S1Angle::Radians(M_PI))) {
+ // The "min" calculation above is necessary to handle S1Angle::Infinity().
+ S2_DCHECK(is_valid());
+}
+
+inline S2Cap::S2Cap(const S2Point& center, S1ChordAngle radius)
+ : center_(center), radius_(radius) {
+ S2_DCHECK(is_valid());
+}
+
+inline S2Cap S2Cap::FromPoint(const S2Point& center) {
+ return S2Cap(center, S1ChordAngle::Zero());
+}
+
+inline S2Cap S2Cap::FromCenterHeight(const S2Point& center, double height) {
+ return S2Cap(center, S1ChordAngle::FromLength2(2 * height));
+}
+
+inline S2Cap S2Cap::FromCenterArea(const S2Point& center, double area) {
+ return S2Cap(center, S1ChordAngle::FromLength2(area / M_PI));
+}
+
+inline S2Cap S2Cap::Empty() { return S2Cap(); }
+
+inline S2Cap S2Cap::Full() {
+ return S2Cap(S2Point(1, 0, 0), S1ChordAngle::Straight());
+}
+
+inline double S2Cap::height() const {
+ return 0.5 * radius_.length2();
+}
+
+inline S1Angle S2Cap::GetRadius() const {
+ return radius_.ToAngle();
+}
+
+inline bool S2Cap::is_valid() const {
+ return S2::IsUnitLength(center_) && radius_.length2() <= 4;
+}
+
+inline bool S2Cap::is_empty() const {
+ return radius_.is_negative();
+}
+
+inline bool S2Cap::is_full() const {
+ return radius_.length2() == 4;
+}
+
+#endif // S2_S2CAP_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2CELL_H_
+#define S2_S2CELL_H_
+
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/r2rect.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2region.h"
+#include "s2/util/math/vector.h"
+
+class Decoder;
+class Encoder;
+class S2Cap;
+class S2LatLng;
+class S2LatLngRect;
+
+// An S2Cell is an S2Region object that represents a cell. Unlike S2CellIds,
+// it supports efficient containment and intersection tests. However, it is
+// also a more expensive representation (currently 48 bytes rather than 8).
+
+// This class is intended to be copied by value as desired. It uses
+// the default copy constructor and assignment operator, however it is
+// not a "plain old datatype" (POD) because it has virtual functions.
+class S2Cell final : public S2Region {
+ public:
+ // The default constructor is required in order to use freelists.
+ // Cells should otherwise always be constructed explicitly.
+ S2Cell() {}
+
+ // An S2Cell always corresponds to a particular S2CellId. The other
+ // constructors are just convenience methods.
+ explicit S2Cell(S2CellId id);
+
+ // Convenience constructors. The S2LatLng must be normalized.
+ explicit S2Cell(const S2Point& p) : S2Cell(S2CellId(p)) {}
+ explicit S2Cell(const S2LatLng& ll) : S2Cell(S2CellId(ll)) {}
+
+ // Returns the cell corresponding to the given S2 cube face.
+ static S2Cell FromFace(int face) {
+ return S2Cell(S2CellId::FromFace(face));
+ }
+
+ // Returns a cell given its face (range 0..5), Hilbert curve position within
+ // that face (an unsigned integer with S2CellId::kPosBits bits), and level
+ // (range 0..kMaxLevel). The given position will be modified to correspond
+ // to the Hilbert curve position at the center of the returned cell. This
+ // is a static function rather than a constructor in order to indicate what
+ // the arguments represent.
+ static S2Cell FromFacePosLevel(int face, uint64 pos, int level) {
+ return S2Cell(S2CellId::FromFacePosLevel(face, pos, level));
+ }
+
+ S2CellId id() const { return id_; }
+ int face() const { return face_; }
+ int level() const { return level_; }
+ int orientation() const { return orientation_; }
+ bool is_leaf() const { return level_ == S2CellId::kMaxLevel; }
+
+ // These are equivalent to the S2CellId methods, but have a more efficient
+ // implementation since the level has been precomputed.
+ int GetSizeIJ() const;
+ double GetSizeST() const;
+
+ // Returns the k-th vertex of the cell (k = 0,1,2,3). Vertices are returned
+ // in CCW order (lower left, lower right, upper right, upper left in the UV
+ // plane). The points returned by GetVertexRaw are not normalized.
+ // For convenience, the argument is reduced modulo 4 to the range [0..3].
+ S2Point GetVertex(int k) const { return GetVertexRaw(k).Normalize(); }
+ S2Point GetVertexRaw(int k) const;
+
+ // Returns the inward-facing normal of the great circle passing through the
+ // edge from vertex k to vertex k+1 (mod 4). The normals returned by
+ // GetEdgeRaw are not necessarily unit length. For convenience, the
+ // argument is reduced modulo 4 to the range [0..3].
+ S2Point GetEdge(int k) const { return GetEdgeRaw(k).Normalize(); }
+ S2Point GetEdgeRaw(int k) const;
+
+ // If this is not a leaf cell, sets children[0..3] to the four children of
+ // this cell (in traversal order) and return true. Otherwise returns false.
+ // This method is equivalent to the following:
+ //
+ // for (pos=0, id=child_begin(); id != child_end(); id = id.next(), ++pos)
+ // children[pos] = S2Cell(id);
+ //
+ // except that it is more than two times faster.
+ bool Subdivide(S2Cell children[4]) const;
+
+ // Returns the direction vector corresponding to the center in (s,t)-space of
+ // the given cell. This is the point at which the cell is divided into four
+ // subcells; it is not necessarily the centroid of the cell in (u,v)-space
+ // or (x,y,z)-space. The point returned by GetCenterRaw is not necessarily
+ // unit length.
+ S2Point GetCenter() const { return GetCenterRaw().Normalize(); }
+ S2Point GetCenterRaw() const;
+
+ // Returns the average area for cells at the given level.
+ static double AverageArea(int level);
+
+ // Returns the average area of cells at this level in steradians. This is
+ // accurate to within a factor of 1.7 (for S2_QUADRATIC_PROJECTION) and is
+ // extremely cheap to compute.
+ double AverageArea() const { return AverageArea(level_); }
+
+ // Returns the approximate area of this cell in steradians. This method is
+ // accurate to within 3% percent for all cell sizes and accurate to within
+ // 0.1% for cells at level 5 or higher (i.e. squares 350km to a side or
+ // smaller on the Earth's surface). It is moderately cheap to compute.
+ double ApproxArea() const;
+
+ // Returns the area of this cell as accurately as possible. This method is
+ // more expensive but it is accurate to 6 digits of precision even for leaf
+ // cells (whose area is approximately 1e-18).
+ double ExactArea() const;
+
+ // Returns the bounds of this cell in (u,v)-space.
+ R2Rect GetBoundUV() const { return uv_; }
+
+ // Returns the distance from the cell to the given point. Returns zero if
+ // the point is inside the cell.
+ S1ChordAngle GetDistance(const S2Point& target) const;
+
+ // Return the distance from the cell boundary to the given point.
+ S1ChordAngle GetBoundaryDistance(const S2Point& target) const;
+
+ // Returns the maximum distance from the cell (including its interior) to the
+ // given point.
+ S1ChordAngle GetMaxDistance(const S2Point& target) const;
+
+ // Returns the minimum distance from the cell to the given edge AB. Returns
+ // zero if the edge intersects the cell interior.
+ S1ChordAngle GetDistance(const S2Point& a, const S2Point& b) const;
+
+ // Returns the maximum distance from the cell (including its interior) to the
+ // given edge AB.
+ S1ChordAngle GetMaxDistance(const S2Point& a, const S2Point& b) const;
+
+ // Returns the distance from the cell to the given cell. Returns zero if
+ // one cell contains the other.
+ S1ChordAngle GetDistance(const S2Cell& target) const;
+
+ // Returns the maximum distance from the cell (including its interior) to the
+ // given target cell.
+ S1ChordAngle GetMaxDistance(const S2Cell& target) const;
+
+ ////////////////////////////////////////////////////////////////////////
+ // S2Region interface (see s2region.h for details):
+
+ S2Cell* Clone() const override;
+ S2Cap GetCapBound() const override;
+ S2LatLngRect GetRectBound() const override;
+ bool Contains(const S2Cell& cell) const override;
+ bool MayIntersect(const S2Cell& cell) const override;
+
+ // Returns true if the cell contains the given point "p". Note that unlike
+ // S2Loop/S2Polygon, S2Cells are considered to be closed sets. This means
+ // that points along an S2Cell edge (or at a vertex) belong to the adjacent
+ // cell(s) as well.
+ //
+ // If instead you want every point to be contained by exactly one S2Cell,
+ // you will need to convert the S2Cells to S2Loops (which implement point
+ // containment this way).
+ //
+ // The point "p" does not need to be normalized.
+ bool Contains(const S2Point& p) const override;
+
+ // Appends a serialized representation of the S2Cell to "encoder".
+ //
+ // REQUIRES: "encoder" uses the default constructor, so that its buffer
+ // can be enlarged as necessary by calling Ensure(int).
+ void Encode(Encoder* const encoder) const;
+
+ // Decodes an S2Cell encoded with Encode(). Returns true on success.
+ bool Decode(Decoder* const decoder);
+
+ private:
+ // Returns the latitude or longitude of the cell vertex given by (i,j),
+ // where "i" and "j" are either 0 or 1.
+ double GetLatitude(int i, int j) const;
+ double GetLongitude(int i, int j) const;
+
+ S1ChordAngle VertexChordDist(const S2Point& p, int i, int j) const;
+ bool UEdgeIsClosest(const S2Point& target, int v_end) const;
+ bool VEdgeIsClosest(const S2Point& target, int u_end) const;
+
+ // Returns the distance from the given point to the interior of the cell if
+ // "to_interior" is true, and to the boundary of the cell otherwise.
+ S1ChordAngle GetDistanceInternal(const S2Point& target_xyz,
+ bool to_interior) const;
+
+ // This structure occupies 44 bytes plus one pointer for the vtable.
+ int8 face_;
+ int8 level_;
+ int8 orientation_;
+ S2CellId id_;
+ R2Rect uv_;
+};
+
+inline bool operator==(const S2Cell& x, const S2Cell& y) {
+ return x.id() == y.id();
+}
+
+inline bool operator!=(const S2Cell& x, const S2Cell& y) {
+ return x.id() != y.id();
+}
+
+inline bool operator<(const S2Cell& x, const S2Cell& y) {
+ return x.id() < y.id();
+}
+
+inline bool operator>(const S2Cell& x, const S2Cell& y) {
+ return x.id() > y.id();
+}
+
+inline bool operator<=(const S2Cell& x, const S2Cell& y) {
+ return x.id() <= y.id();
+}
+
+inline bool operator>=(const S2Cell& x, const S2Cell& y) {
+ return x.id() >= y.id();
+}
+
+inline int S2Cell::GetSizeIJ() const {
+ return S2CellId::GetSizeIJ(level());
+}
+
+inline double S2Cell::GetSizeST() const {
+ return S2CellId::GetSizeST(level());
+}
+
+#endif // S2_S2CELL_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2CELL_ID_H_
+#define S2_S2CELL_ID_H_
+
+#include <cstddef>
+#include <functional>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+#include "s2/base/port.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/r2.h"
+#include "s2/r2rect.h"
+#include "s2/s1angle.h"
+#include "s2/s2coords.h"
+#include "s2/third_party/absl/strings/string_view.h"
+#include "s2/util/bits/bits.h"
+#include "s2/util/coding/coder.h"
+
+class S2LatLng;
+
+#ifndef SWIG
+#define IFNDEF_SWIG(x) x
+#else
+#define IFNDEF_SWIG(x)
+#endif
+
+// An S2CellId is a 64-bit unsigned integer that uniquely identifies a
+// cell in the S2 cell decomposition. It has the following format:
+//
+// id = [face][face_pos]
+//
+// face: a 3-bit number (range 0..5) encoding the cube face.
+//
+// face_pos: a 61-bit number encoding the position of the center of this
+// cell along the Hilbert curve over this face (see the Wiki
+// pages for details).
+//
+// Sequentially increasing cell ids follow a continuous space-filling curve
+// over the entire sphere. They have the following properties:
+//
+// - The id of a cell at level k consists of a 3-bit face number followed
+// by k bit pairs that recursively select one of the four children of
+// each cell. The next bit is always 1, and all other bits are 0.
+// Therefore, the level of a cell is determined by the position of its
+// lowest-numbered bit that is turned on (for a cell at level k, this
+// position is 2 * (kMaxLevel - k).)
+//
+// - The id of a parent cell is at the midpoint of the range of ids spanned
+// by its children (or by its descendants at any level).
+//
+// Leaf cells are often used to represent points on the unit sphere, and
+// this class provides methods for converting directly between these two
+// representations. For cells that represent 2D regions rather than
+// discrete point, it is better to use the S2Cell class.
+//
+// This class is intended to be copied by value as desired. It uses
+// the default copy constructor and assignment operator.
+class S2CellId {
+ public:
+ // The extra position bit (61 rather than 60) let us encode each cell as its
+ // Hilbert curve position at the cell center (which is halfway along the
+ // portion of the Hilbert curve that fills that cell).
+ static const int kFaceBits = 3;
+ static const int kNumFaces = 6;
+ static const int kMaxLevel = S2::kMaxCellLevel; // Valid levels: 0..kMaxLevel
+ static const int kPosBits = 2 * kMaxLevel + 1;
+ static const int kMaxSize = 1 << kMaxLevel;
+
+ explicit IFNDEF_SWIG(constexpr) S2CellId(uint64 id) : id_(id) {}
+
+ // Construct a leaf cell containing the given point "p". Usually there is
+ // is exactly one such cell, but for points along the edge of a cell, any
+ // adjacent cell may be (deterministically) chosen. This is because
+ // S2CellIds are considered to be closed sets. The returned cell will
+ // always contain the given point, i.e.
+ //
+ // S2Cell(S2CellId(p)).Contains(p)
+ //
+ // is always true. The point "p" does not need to be normalized.
+ //
+ // If instead you want every point to be contained by exactly one S2Cell,
+ // you will need to convert the S2CellIds to S2Loops (which implement point
+ // containment this way).
+ explicit S2CellId(const S2Point& p);
+
+ // Construct a leaf cell containing the given normalized S2LatLng.
+ explicit S2CellId(const S2LatLng& ll);
+
+ // The default constructor returns an invalid cell id.
+ IFNDEF_SWIG(constexpr) S2CellId() : id_(0) {}
+ static constexpr S2CellId None() { return S2CellId(); }
+
+ // Returns an invalid cell id guaranteed to be larger than any
+ // valid cell id. Useful for creating indexes.
+ static constexpr S2CellId Sentinel() { return S2CellId(~uint64{0}); }
+
+ // Return the cell corresponding to a given S2 cube face.
+ static S2CellId FromFace(int face);
+
+ // Return a cell given its face (range 0..5), Hilbert curve position within
+ // that face (an unsigned integer with S2CellId::kPosBits bits), and level
+ // (range 0..kMaxLevel). The given position will be modified to correspond
+ // to the Hilbert curve position at the center of the returned cell. This
+ // is a static function rather than a constructor in order to indicate what
+ // the arguments represent.
+ static S2CellId FromFacePosLevel(int face, uint64 pos, int level);
+
+ // Return the direction vector corresponding to the center of the given
+ // cell. The vector returned by ToPointRaw is not necessarily unit length.
+ // This method returns the same result as S2Cell::GetCenter().
+ //
+ // The maximum directional error in ToPoint() (compared to the exact
+ // mathematical result) is 1.5 * DBL_EPSILON radians, and the maximum length
+ // error is 2 * DBL_EPSILON (the same as Normalize).
+ S2Point ToPoint() const { return ToPointRaw().Normalize(); }
+ S2Point ToPointRaw() const;
+
+ // Return the center of the cell in (s,t) coordinates (see s2coords.h).
+ R2Point GetCenterST() const;
+
+ // Return the edge length of this cell in (s,t)-space.
+ double GetSizeST() const;
+
+ // Return the edge length in (s,t)-space of cells at the given level.
+ static double GetSizeST(int level);
+
+ // Return the bounds of this cell in (s,t)-space.
+ R2Rect GetBoundST() const;
+
+ // Return the center of the cell in (u,v) coordinates (see s2coords.h).
+ // Note that the center of the cell is defined as the point at which it is
+ // recursively subdivided into four children; in general, it is not at the
+ // midpoint of the (u,v) rectangle covered by the cell.
+ R2Point GetCenterUV() const;
+
+ // Return the bounds of this cell in (u,v)-space.
+ R2Rect GetBoundUV() const;
+
+ // Expand a rectangle in (u,v)-space so that it contains all points within
+ // the given distance of the boundary, and return the smallest such
+ // rectangle. If the distance is negative, then instead shrink this
+ // rectangle so that it excludes all points within the given absolute
+ // distance of the boundary.
+ //
+ // Distances are measured *on the sphere*, not in (u,v)-space. For example,
+ // you can use this method to expand the (u,v)-bound of an S2CellId so that
+ // it contains all points within 5km of the original cell. You can then
+ // test whether a point lies within the expanded bounds like this:
+ //
+ // R2Point uv;
+ // if (S2::FaceXYZtoUV(face, point, &uv) && bound.Contains(uv)) { ... }
+ //
+ // Limitations:
+ //
+ // - Because the rectangle is drawn on one of the six cube-face planes
+ // (i.e., {x,y,z} = +/-1), it can cover at most one hemisphere. This
+ // limits the maximum amount that a rectangle can be expanded. For
+ // example, S2CellId bounds can be expanded safely by at most 45 degrees
+ // (about 5000 km on the Earth's surface).
+ //
+ // - The implementation is not exact for negative distances. The resulting
+ // rectangle will exclude all points within the given distance of the
+ // boundary but may be slightly smaller than necessary.
+ static R2Rect ExpandedByDistanceUV(const R2Rect& uv, S1Angle distance);
+
+ // Return the (face, si, ti) coordinates of the center of the cell. Note
+ // that although (si,ti) coordinates span the range [0,2**31] in general,
+ // the cell center coordinates are always in the range [1,2**31-1] and
+ // therefore can be represented using a signed 32-bit integer.
+ int GetCenterSiTi(int* psi, int* pti) const;
+
+ // Return the S2LatLng corresponding to the center of the given cell.
+ S2LatLng ToLatLng() const;
+
+ // The 64-bit unique identifier for this cell.
+ uint64 id() const { return id_; }
+
+ // Return true if id() represents a valid cell.
+ //
+ // All methods require is_valid() to be true unless otherwise specified
+ // (although not all methods enforce this).
+ bool is_valid() const;
+
+ // Which cube face this cell belongs to, in the range 0..5.
+ int face() const;
+
+ // The position of the cell center along the Hilbert curve over this face,
+ // in the range 0..(2**kPosBits-1).
+ uint64 pos() const;
+
+ // Return the subdivision level of the cell (range 0..kMaxLevel).
+ int level() const;
+
+ // Return the edge length of this cell in (i,j)-space.
+ int GetSizeIJ() const;
+
+ // Like the above, but return the size of cells at the given level.
+ static int GetSizeIJ(int level);
+
+ // Return true if this is a leaf cell (more efficient than checking
+ // whether level() == kMaxLevel).
+ bool is_leaf() const;
+
+ // Return true if this is a top-level face cell (more efficient than
+ // checking whether level() == 0).
+ bool is_face() const;
+
+ // Return the child position (0..3) of this cell within its parent.
+ // REQUIRES: level() >= 1.
+ int child_position() const;
+
+ // Return the child position (0..3) of this cell's ancestor at the given
+ // level within its parent. For example, child_position(1) returns the
+ // position of this cell's level-1 ancestor within its top-level face cell.
+ // REQUIRES: 1 <= level <= this->level().
+ int child_position(int level) const;
+
+ // These methods return the range of cell ids that are contained within this
+ // cell (including itself). The range is *inclusive* (i.e. test using >=
+ // and <=) and the return values of both methods are valid leaf cell ids.
+ // In other words, a.contains(b) if and only if
+ //
+ // (b >= a.range_min() && b <= a.range_max())
+ //
+ // If you want to iterate through all the descendants of this cell at a
+ // particular level, use child_begin(level) and child_end(level) instead.
+ // Also see maximum_tile(), which can be used to iterate through a range of
+ // cells using S2CellIds at different levels that are as large as possible.
+ //
+ // If you need to convert the range to a semi-open interval [min, limit)
+ // (e.g., in order to use a key-value store that only supports semi-open
+ // range queries), do not attempt to define "limit" as range_max.next().
+ // The problem is that leaf S2CellIds are 2 units apart, so the semi-open
+ // interval [min, limit) includes an additional value (range_max.id() + 1)
+ // which is happens to be a valid S2CellId about one-third of the time and
+ // is *never* contained by this cell. (It always correpsonds to a cell that
+ // is larger than this one.) You can define "limit" as (range_max.id() + 1)
+ // if necessary (which is not always a valid S2CellId but can still be used
+ // with FromToken/ToToken), or you can convert range_max() to the key space
+ // of your key-value store and define "limit" as Successor(key).
+ //
+ // Note that Sentinel().range_min() == Sentinel.range_max() == Sentinel().
+ S2CellId range_min() const;
+ S2CellId range_max() const;
+
+ // Return true if the given cell is contained within this one.
+ bool contains(S2CellId other) const;
+
+ // Return true if the given cell intersects this one.
+ bool intersects(S2CellId other) const;
+
+ // Return the cell at the previous level or at the given level (which must
+ // be less than or equal to the current level).
+ S2CellId parent() const;
+ S2CellId parent(int level) const;
+
+ // Return the immediate child of this cell at the given traversal order
+ // position (in the range 0 to 3). This cell must not be a leaf cell.
+ S2CellId child(int position) const;
+
+ // Iterator-style methods for traversing the immediate children of a cell or
+ // all of the children at a given level (greater than or equal to the current
+ // level). Note that the end value is exclusive, just like standard STL
+ // iterators, and may not even be a valid cell id. You should iterate using
+ // code like this:
+ //
+ // for(S2CellId c = id.child_begin(); c != id.child_end(); c = c.next())
+ // ...
+ //
+ // The convention for advancing the iterator is "c = c.next()" rather
+ // than "++c" to avoid possible confusion with incrementing the
+ // underlying 64-bit cell id.
+ S2CellId child_begin() const;
+ S2CellId child_begin(int level) const;
+ S2CellId child_end() const;
+ S2CellId child_end(int level) const;
+
+ // Return the next/previous cell at the same level along the Hilbert curve.
+ // Works correctly when advancing from one face to the next, but
+ // does *not* wrap around from the last face to the first or vice versa.
+ S2CellId next() const;
+ S2CellId prev() const;
+
+ // This method advances or retreats the indicated number of steps along the
+ // Hilbert curve at the current level, and returns the new position. The
+ // position is never advanced past End() or before Begin().
+ S2CellId advance(int64 steps) const;
+
+ // Returns the number of steps that this cell is from Begin(level()). The
+ // return value is always non-negative.
+ int64 distance_from_begin() const;
+
+ // Like next() and prev(), but these methods wrap around from the last face
+ // to the first and vice versa. They should *not* be used for iteration in
+ // conjunction with child_begin(), child_end(), Begin(), or End(). The
+ // input must be a valid cell id.
+ S2CellId next_wrap() const;
+ S2CellId prev_wrap() const;
+
+ // This method advances or retreats the indicated number of steps along the
+ // Hilbert curve at the current level, and returns the new position. The
+ // position wraps between the first and last faces as necessary. The input
+ // must be a valid cell id.
+ S2CellId advance_wrap(int64 steps) const;
+
+ // Return the largest cell with the same range_min() and such that
+ // range_max() < limit.range_min(). Returns "limit" if no such cell exists.
+ // This method can be used to generate a small set of S2CellIds that covers
+ // a given range (a "tiling"). This example shows how to generate a tiling
+ // for a semi-open range of leaf cells [start, limit):
+ //
+ // for (S2CellId id = start.maximum_tile(limit);
+ // id != limit; id = id.next().maximum_tile(limit)) { ... }
+ //
+ // Note that in general the cells in the tiling will be of different sizes;
+ // they gradually get larger (near the middle of the range) and then
+ // gradually get smaller (as "limit" is approached).
+ S2CellId maximum_tile(S2CellId limit) const;
+
+ // Returns the level of the lowest common ancestor of this cell and "other",
+ // that is, the maximum level such that parent(level) == other.parent(level).
+ // Returns -1 if the two cells do not have any common ancestor (i.e., they
+ // are from different faces).
+ int GetCommonAncestorLevel(S2CellId other) const;
+
+ // Iterator-style methods for traversing all the cells along the Hilbert
+ // curve at a given level (across all 6 faces of the cube). Note that the
+ // end value is exclusive (just like standard STL iterators), and is not a
+ // valid cell id.
+ static S2CellId Begin(int level);
+ static S2CellId End(int level);
+
+ // Methods to encode and decode cell ids to compact text strings suitable
+ // for display or indexing. Cells at lower levels (i.e. larger cells) are
+ // encoded into fewer characters. The maximum token length is 16.
+ //
+ // Tokens preserve ordering, i.e. ToToken(x) < ToToken(y) iff x < y.
+ //
+ // ToToken() returns a string by value for convenience; the compiler
+ // does this without intermediate copying in most cases.
+ //
+ // These methods guarantee that FromToken(ToToken(x)) == x even when
+ // "x" is an invalid cell id. All tokens are alphanumeric strings.
+ // FromToken() returns S2CellId::None() for malformed inputs.
+ string ToToken() const;
+ static S2CellId FromToken(const char* token, size_t length);
+ static S2CellId FromToken(const string& token);
+
+ // Use encoder to generate a serialized representation of this cell id.
+ // Can also encode an invalid cell.
+ void Encode(Encoder* const encoder) const;
+
+ // Decodes an S2CellId encoded by Encode(). Returns true on success.
+ bool Decode(Decoder* const decoder);
+
+ // Creates a human readable debug string. Used for << and available for
+ // direct usage as well. The format is "f/dd..d" where "f" is a digit in
+ // the range [0-5] representing the S2CellId face, and "dd..d" is a string
+ // of digits in the range [0-3] representing each child's position with
+ // respect to its parent. (Note that the latter string may be empty.)
+ //
+ // For example "4/" represents S2CellId::FromFace(4), and "3/02" represents
+ // S2CellId::FromFace(3).child(0).child(2).
+ string ToString() const;
+
+ // Converts a string in the format returned by ToString() to an S2CellId.
+ // Returns S2CellId::None() if the string could not be parsed.
+ //
+ // The method name includes "Debug" in order to avoid possible confusion
+ // with FromToken() above.
+ static S2CellId FromDebugString(absl::string_view str);
+
+ // Return the four cells that are adjacent across the cell's four edges.
+ // Neighbors are returned in the order defined by S2Cell::GetEdge. All
+ // neighbors are guaranteed to be distinct.
+ void GetEdgeNeighbors(S2CellId neighbors[4]) const;
+
+ // Return the neighbors of closest vertex to this cell at the given level,
+ // by appending them to "output". Normally there are four neighbors, but
+ // the closest vertex may only have three neighbors if it is one of the 8
+ // cube vertices.
+ //
+ // Requires: level < this->level(), so that we can determine which vertex is
+ // closest (in particular, level == kMaxLevel is not allowed).
+ void AppendVertexNeighbors(int level, std::vector<S2CellId>* output) const;
+
+ // Append all neighbors of this cell at the given level to "output". Two
+ // cells X and Y are neighbors if their boundaries intersect but their
+ // interiors do not. In particular, two cells that intersect at a single
+ // point are neighbors. Note that for cells adjacent to a face vertex, the
+ // same neighbor may be appended more than once.
+ //
+ // REQUIRES: nbr_level >= this->level().
+ void AppendAllNeighbors(int nbr_level, std::vector<S2CellId>* output) const;
+
+ /////////////////////////////////////////////////////////////////////
+ // Low-level methods.
+
+ // Return a leaf cell given its cube face (range 0..5) and
+ // i- and j-coordinates (see s2coords.h).
+ static S2CellId FromFaceIJ(int face, int i, int j);
+
+ // Return the (face, i, j) coordinates for the leaf cell corresponding to
+ // this cell id. Since cells are represented by the Hilbert curve position
+ // at the center of the cell, the returned (i,j) for non-leaf cells will be
+ // a leaf cell adjacent to the cell center. If "orientation" is non-nullptr,
+ // also return the Hilbert curve orientation for the current cell.
+ int ToFaceIJOrientation(int* pi, int* pj, int* orientation) const;
+
+ // Return the lowest-numbered bit that is on for this cell id, which is
+ // equal to (uint64{1} << (2 * (kMaxLevel - level))). So for example,
+ // a.lsb() <= b.lsb() if and only if a.level() >= b.level(), but the
+ // first test is more efficient.
+ uint64 lsb() const { return id_ & (~id_ + 1); }
+
+ // Return the lowest-numbered bit that is on for cells at the given level.
+ static uint64 lsb_for_level(int level) {
+ return uint64{1} << (2 * (kMaxLevel - level));
+ }
+
+ // Return the bound in (u,v)-space for the cell at the given level containing
+ // the leaf cell with the given (i,j)-coordinates.
+ static R2Rect IJLevelToBoundUV(int ij[2], int level);
+
+ // When S2CellId is used as a key in one of the btree container types
+ // (util/btree), indicate that linear rather than binary search should be
+ // used. This is much faster when the comparison function is cheap.
+ typedef std::true_type goog_btree_prefer_linear_node_search;
+
+ private:
+ // This is the offset required to wrap around from the beginning of the
+ // Hilbert curve to the end or vice versa; see next_wrap() and prev_wrap().
+ // SWIG doesn't understand uint64{}, so use static_cast.
+ static const uint64 kWrapOffset = static_cast<uint64>(kNumFaces) << kPosBits;
+
+ // Given a face and a point (i,j) where either i or j is outside the valid
+ // range [0..kMaxSize-1], this function first determines which neighboring
+ // face "contains" (i,j), and then returns the leaf cell on that face which
+ // is adjacent to the given face and whose distance from (i,j) is minimal.
+ static S2CellId FromFaceIJWrap(int face, int i, int j);
+
+ // Inline helper function that calls FromFaceIJ if "same_face" is true,
+ // or FromFaceIJWrap if "same_face" is false.
+ static S2CellId FromFaceIJSame(int face, int i, int j, bool same_face);
+
+ uint64 id_;
+} ABSL_ATTRIBUTE_PACKED; // Necessary so that structures containing S2CellId's
+ // can be ABSL_ATTRIBUTE_PACKED.
+
+inline bool operator==(S2CellId x, S2CellId y) {
+ return x.id() == y.id();
+}
+
+inline bool operator!=(S2CellId x, S2CellId y) {
+ return x.id() != y.id();
+}
+
+inline bool operator<(S2CellId x, S2CellId y) {
+ return x.id() < y.id();
+}
+
+inline bool operator>(S2CellId x, S2CellId y) {
+ return x.id() > y.id();
+}
+
+inline bool operator<=(S2CellId x, S2CellId y) {
+ return x.id() <= y.id();
+}
+
+inline bool operator>=(S2CellId x, S2CellId y) {
+ return x.id() >= y.id();
+}
+
+inline S2CellId S2CellId::FromFace(int face) {
+ return S2CellId((static_cast<uint64>(face) << kPosBits) + lsb_for_level(0));
+}
+
+inline S2CellId S2CellId::FromFacePosLevel(int face, uint64 pos, int level) {
+ S2CellId cell((static_cast<uint64>(face) << kPosBits) + (pos | 1));
+ return cell.parent(level);
+}
+
+inline int S2CellId::GetCenterSiTi(int* psi, int* pti) const {
+ // First we compute the discrete (i,j) coordinates of a leaf cell contained
+ // within the given cell. Given that cells are represented by the Hilbert
+ // curve position corresponding at their center, it turns out that the cell
+ // returned by ToFaceIJOrientation is always one of two leaf cells closest
+ // to the center of the cell (unless the given cell is a leaf cell itself,
+ // in which case there is only one possibility).
+ //
+ // Given a cell of size s >= 2 (i.e. not a leaf cell), and letting (imin,
+ // jmin) be the coordinates of its lower left-hand corner, the leaf cell
+ // returned by ToFaceIJOrientation() is either (imin + s/2, jmin + s/2)
+ // (imin + s/2 - 1, jmin + s/2 - 1). The first case is the one we want.
+ // We can distinguish these two cases by looking at the low bit of "i" or
+ // "j". In the second case the low bit is one, unless s == 2 (i.e. the
+ // level just above leaf cells) in which case the low bit is zero.
+ //
+ // In the code below, the expression ((i ^ (int(id_) >> 2)) & 1) is true
+ // if we are in the second case described above.
+ int i, j;
+ int face = ToFaceIJOrientation(&i, &j, nullptr);
+ int delta = is_leaf() ? 1 : ((i ^ (static_cast<int>(id_) >> 2)) & 1) ? 2 : 0;
+
+ // Note that (2 * {i,j} + delta) will never overflow a 32-bit integer.
+ *psi = 2 * i + delta;
+ *pti = 2 * j + delta;
+ return face;
+}
+
+inline bool S2CellId::is_valid() const {
+ return (face() < kNumFaces && (lsb() & 0x1555555555555555ULL));
+}
+
+inline int S2CellId::face() const {
+ return id_ >> kPosBits;
+}
+
+inline uint64 S2CellId::pos() const {
+ return id_ & (~uint64{0} >> kFaceBits);
+}
+
+inline int S2CellId::level() const {
+ // We can't just S2_DCHECK(is_valid()) because we want level() to be
+ // defined for end-iterators, i.e. S2CellId::End(kLevel). However there is
+ // no good way to define S2CellId::None().level(), so we do prohibit that.
+ S2_DCHECK(id_ != 0);
+
+ // A special case for leaf cells is not worthwhile.
+ return kMaxLevel - (Bits::FindLSBSetNonZero64(id_) >> 1);
+}
+
+inline int S2CellId::GetSizeIJ() const {
+ return GetSizeIJ(level());
+}
+
+inline double S2CellId::GetSizeST() const {
+ return GetSizeST(level());
+}
+
+inline int S2CellId::GetSizeIJ(int level) {
+ return 1 << (kMaxLevel - level);
+}
+
+inline double S2CellId::GetSizeST(int level) {
+ return S2::IJtoSTMin(GetSizeIJ(level));
+}
+
+inline bool S2CellId::is_leaf() const {
+ return int(id_) & 1;
+}
+
+inline bool S2CellId::is_face() const {
+ return (id_ & (lsb_for_level(0) - 1)) == 0;
+}
+
+inline int S2CellId::child_position() const {
+ // No need for a special implementation; the compiler optimizes this well.
+ return child_position(level());
+}
+
+inline int S2CellId::child_position(int level) const {
+ S2_DCHECK(is_valid());
+ S2_DCHECK_GE(level, 1);
+ S2_DCHECK_LE(level, this->level());
+ return static_cast<int>(id_ >> (2 * (kMaxLevel - level) + 1)) & 3;
+}
+
+inline S2CellId S2CellId::range_min() const {
+ return S2CellId(id_ - (lsb() - 1));
+}
+
+inline S2CellId S2CellId::range_max() const {
+ return S2CellId(id_ + (lsb() - 1));
+}
+
+inline bool S2CellId::contains(S2CellId other) const {
+ S2_DCHECK(is_valid());
+ S2_DCHECK(other.is_valid());
+ return other >= range_min() && other <= range_max();
+}
+
+inline bool S2CellId::intersects(S2CellId other) const {
+ S2_DCHECK(is_valid());
+ S2_DCHECK(other.is_valid());
+ return other.range_min() <= range_max() && other.range_max() >= range_min();
+}
+
+inline S2CellId S2CellId::parent(int level) const {
+ S2_DCHECK(is_valid());
+ S2_DCHECK_GE(level, 0);
+ S2_DCHECK_LE(level, this->level());
+ uint64 new_lsb = lsb_for_level(level);
+ return S2CellId((id_ & (~new_lsb + 1)) | new_lsb);
+}
+
+inline S2CellId S2CellId::parent() const {
+ S2_DCHECK(is_valid());
+ S2_DCHECK(!is_face());
+ uint64 new_lsb = lsb() << 2;
+ return S2CellId((id_ & (~new_lsb + 1)) | new_lsb);
+}
+
+inline S2CellId S2CellId::child(int position) const {
+ S2_DCHECK(is_valid());
+ S2_DCHECK(!is_leaf());
+ // To change the level, we need to move the least-significant bit two
+ // positions downward. We do this by subtracting (4 * new_lsb) and adding
+ // new_lsb. Then to advance to the given child cell, we add
+ // (2 * position * new_lsb).
+ uint64 new_lsb = lsb() >> 2;
+ return S2CellId(id_ + (2 * position + 1 - 4) * new_lsb);
+}
+
+inline S2CellId S2CellId::child_begin() const {
+ S2_DCHECK(is_valid());
+ S2_DCHECK(!is_leaf());
+ uint64 old_lsb = lsb();
+ return S2CellId(id_ - old_lsb + (old_lsb >> 2));
+}
+
+inline S2CellId S2CellId::child_begin(int level) const {
+ S2_DCHECK(is_valid());
+ S2_DCHECK_GE(level, this->level());
+ S2_DCHECK_LE(level, kMaxLevel);
+ return S2CellId(id_ - lsb() + lsb_for_level(level));
+}
+
+inline S2CellId S2CellId::child_end() const {
+ S2_DCHECK(is_valid());
+ S2_DCHECK(!is_leaf());
+ uint64 old_lsb = lsb();
+ return S2CellId(id_ + old_lsb + (old_lsb >> 2));
+}
+
+inline S2CellId S2CellId::child_end(int level) const {
+ S2_DCHECK(is_valid());
+ S2_DCHECK_GE(level, this->level());
+ S2_DCHECK_LE(level, kMaxLevel);
+ return S2CellId(id_ + lsb() + lsb_for_level(level));
+}
+
+inline S2CellId S2CellId::next() const {
+ return S2CellId(id_ + (lsb() << 1));
+}
+
+inline S2CellId S2CellId::prev() const {
+ return S2CellId(id_ - (lsb() << 1));
+}
+
+inline S2CellId S2CellId::next_wrap() const {
+ S2_DCHECK(is_valid());
+ S2CellId n = next();
+ if (n.id_ < kWrapOffset) return n;
+ return S2CellId(n.id_ - kWrapOffset);
+}
+
+inline S2CellId S2CellId::prev_wrap() const {
+ S2_DCHECK(is_valid());
+ S2CellId p = prev();
+ if (p.id_ < kWrapOffset) return p;
+ return S2CellId(p.id_ + kWrapOffset);
+}
+
+inline S2CellId S2CellId::Begin(int level) {
+ return FromFace(0).child_begin(level);
+}
+
+inline S2CellId S2CellId::End(int level) {
+ return FromFace(5).child_end(level);
+}
+
+std::ostream& operator<<(std::ostream& os, S2CellId id);
+
+// Hasher for S2CellId.
+// Example use: std::unordered_map<S2CellId, int, S2CellIdHash>.
+struct S2CellIdHash {
+ size_t operator()(S2CellId id) const {
+ return std::hash<uint64>()(id.id());
+ }
+};
+
+#undef IFNDEF_SWIG
+
+#endif // S2_S2CELL_ID_H_
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2CELL_INDEX_H_
+#define S2_S2CELL_INDEX_H_
+
+#include <vector>
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2cell_union.h"
+
+// S2CellIndex stores a collection of (cell_id, label) pairs. The S2CellIds
+// may be overlapping or contain duplicate values. For example, an
+// S2CellIndex could store a collection of S2CellUnions, where each
+// S2CellUnion has its own label.
+//
+// Labels are 32-bit non-negative integers, and are typically used to map the
+// results of queries back to client data structures. Labels other than
+// integers can be supported by using a ValueLexicon, which maintains a set of
+// distinct labels and maps them to sequentially numbered integers. For
+// example, the following code uses strings as labels:
+//
+// ValueLexicon<string> my_label_lexicon;
+// string label_str = ...;
+// cell_index.Add(cell_id, my_label_lexicon.Add(label_str));
+// ...
+// int32 label = ...;
+// string label_str = my_label_lexicon.value(label);
+//
+// To build an S2CellIndex, call Add() for each (cell_id, label) pair, and
+// then call the Build() method. For example:
+//
+// vector<S2CellId> contents = ...;
+// for (int i = 0; i < contents.size(); ++i) {
+// index.Add(contents[i], i /*label*/);
+// }
+// index.Build();
+//
+// There is also a convenience method that adds an S2CellUnion:
+//
+// index.Add(cell_union, label);
+//
+// Note that the index is not dynamic; the contents of the index cannot be
+// changed once it has been built.
+//
+// There are several options for retrieving data from the index. The simplest
+// is to use a built-in method such as GetIntersectingLabels (which returns
+// the labels of all cells that intersect a given target S2CellUnion):
+//
+// vector<Label> labels = index.GetIntersectingLabels(target_union);
+//
+// Alternatively, you can use an external class such as S2ClosestCellQuery,
+// which computes the cell(s) that are closest to a given target geometry.
+// For example, here is how to find all cells that are closer than
+// "distance_limit" to a given target point:
+//
+// S2ClosestCellQuery query(&index);
+// query.mutable_options()->set_max_distance(distance_limit);
+// S2ClosestCellQuery::PointTarget target(target_point);
+// for (const auto& result : query.FindClosestCells(&target)) {
+// // result.distance() is the distance to the target.
+// // result.cell_id() is the indexed S2CellId.
+// // result.label() is the integer label associated with the S2CellId.
+// DoSomething(target_point, result);
+// }
+//
+// Finally, you can access the index contents directly. Internally, the index
+// consists of a set of non-overlapping leaf cell ranges that subdivide the
+// sphere and such that each range intersects a particular set of (cell_id,
+// label) pairs. Data is accessed using the following iterator types:
+//
+// RangeIterator:
+// - used to seek and iterate over the non-overlapping leaf cell ranges.
+// NonEmptyRangeIterator:
+// - like RangeIterator, but skips ranges whose contents are empty.
+// ContentsIterator:
+// - iterates over the (cell_id, label) pairs that intersect a given range.
+// CellIterator:
+// - iterates over the entire set of (cell_id, label) pairs.
+//
+// Note that these are low-level, efficient types intended mainly for
+// implementing new query classes. Most clients should use either the
+// built-in methods such as VisitIntersectingCells and GetIntersectingLabels,
+// or a helper such as S2ClosestCellQuery or S2Closest*Query::CellUnionTarget.
+class S2CellIndex {
+ public:
+ // Labels are 32-bit non-negative integers. To support other label types,
+ // you can use ValueLexicon to map label values to integers:
+ //
+ // ValueLexicon<MyLabel> my_label_lexicon;
+ // index.Add(cell_id, my_label_lexicon.Add(label));
+ using Label = int32;
+
+ // Convenience class that represents a (cell_id, label) pair.
+ struct LabelledCell {
+ S2CellId cell_id;
+ Label label;
+
+ LabelledCell() : cell_id(S2CellId::None()), label(-1) {}
+
+ LabelledCell(S2CellId _cell_id, Label _label)
+ : cell_id(_cell_id), label(_label) {}
+
+ bool operator==(LabelledCell y) const {
+ return cell_id == y.cell_id && label == y.label;
+ }
+
+ bool operator<(LabelledCell y) const {
+ if (cell_id < y.cell_id) return true;
+ if (y.cell_id < cell_id) return false;
+ return label < y.label;
+ }
+ };
+
+ // Default constructor.
+ S2CellIndex();
+
+ // Returns the number of (cell_id, label) pairs in the index.
+ int num_cells() const;
+
+ // Adds the given (cell_id, label) pair to the index. Note that the index
+ // is not valid until Build() is called.
+ //
+ // The S2CellIds in the index may overlap (including duplicate values).
+ // Duplicate (cell_id, label) pairs are also allowed, although be aware that
+ // S2ClosestCellQuery will eliminate such duplicates anyway.
+ //
+ // REQUIRES: cell_id.is_valid()
+ void Add(S2CellId cell_id, Label label);
+
+ // Convenience function that adds a collection of cells with the same label.
+ void Add(const S2CellUnion& cell_ids, Label label);
+
+ // Constructs the index. This method may only be called once. No iterators
+ // may be used until the index is built.
+ void Build();
+
+ // Clears the index so that it can be re-used.
+ void Clear();
+
+ // A function that is called with each (cell_id, label) pair to be visited.
+ // The function may return false in order to indicate that no further
+ // (cell_id, label) pairs are needed.
+ using CellVisitor = std::function<bool (S2CellId cell_id, Label label)>;
+
+ // Visits all (cell_id, label) pairs in the given index that intersect the
+ // given S2CellUnion "target", terminating early if the given CellVisitor
+ // function returns false (in which case VisitIntersectingCells returns false
+ // as well). Each (cell_id, label) pair in the index is visited at most
+ // once. (If the index contains duplicates, then each copy is visited.)
+ bool VisitIntersectingCells(const S2CellUnion& target,
+ const CellVisitor& visitor) const;
+
+ // Convenience function that returns the labels of all indexed cells that
+ // intersect the given S2CellUnion "target". The output contains each label
+ // at most once, but is not sorted.
+ std::vector<Label> GetIntersectingLabels(const S2CellUnion& target) const;
+
+ // This version can be more efficient when it is called many times, since it
+ // does not require allocating a new vector on each call.
+ void GetIntersectingLabels(const S2CellUnion& target,
+ std::vector<Label>* labels) const;
+
+ private:
+ // Represents a node in the set of non-overlapping leaf cell ranges.
+ struct RangeNode;
+
+ // A special label indicating that ContentsIterator::done() is true.
+ static Label constexpr kDoneContents = -1;
+
+ // Represents a node in the (cell_id, label) tree. Cells are organized in a
+ // tree such that the ancestors of a given node contain that node.
+ struct CellNode {
+ S2CellId cell_id;
+ Label label;
+ int32 parent;
+
+ CellNode(S2CellId _cell_id, Label _label, int32 _parent)
+ : cell_id(_cell_id), label(_label), parent(_parent) {
+ }
+ CellNode() : cell_id(S2CellId::None()), label(kDoneContents), parent(-1) {}
+ };
+
+ public:
+ class ContentsIterator;
+
+ // An iterator that visits the entire set of indexed (cell_id, label) pairs
+ // in an unspecified order.
+ class CellIterator {
+ public:
+ // Initializes a CellIterator for the given S2CellIndex, positioned at the
+ // first cell (if any).
+ explicit CellIterator(const S2CellIndex* index);
+
+ // The S2CellId of the current (cell_id, label) pair.
+ // REQUIRES: !done()
+ S2CellId cell_id() const;
+
+ // The Label of the current (cell_id, label) pair.
+ // REQUIRES: !done()
+ Label label() const;
+
+ // Returns the current (cell_id, label) pair.
+ LabelledCell labelled_cell() const;
+
+ // Returns true if all (cell_id, label) pairs have been visited.
+ bool done() const;
+
+ // Advances the iterator to the next (cell_id, label) pair.
+ // REQUIRES: !done()
+ void Next();
+
+ private:
+ // NOTE(ericv): There is a potential optimization that would require this
+ // class to iterate over both cell_tree_ *and* range_nodes_.
+ std::vector<CellNode>::const_iterator cell_it_, cell_end_;
+ };
+
+ // An iterator that seeks and iterates over a set of non-overlapping leaf
+ // cell ranges that cover the entire sphere. The indexed (s2cell_id, label)
+ // pairs that intersect the current leaf cell range can be visited using
+ // ContentsIterator (see below).
+ class RangeIterator {
+ public:
+ // Initializes a RangeIterator for the given S2CellIndex. The iterator is
+ // initially *unpositioned*; you must call a positioning method such as
+ // Begin() or Seek() before accessing its contents.
+ explicit RangeIterator(const S2CellIndex* index);
+
+ // The start of the current range of leaf S2CellIds.
+ //
+ // If done() is true, returns S2CellId::End(S2CellId::kMaxLevel). This
+ // property means that most loops do not need to test done() explicitly.
+ S2CellId start_id() const;
+
+ // The (non-inclusive) end of the current range of leaf S2CellIds.
+ // REQUIRES: !done()
+ S2CellId limit_id() const;
+
+ // Returns true if the iterator is positioned beyond the last valid range.
+ bool done() const;
+
+ // Positions the iterator at the first range of leaf cells (if any).
+ void Begin();
+
+ // Positions the iterator so that done() is true.
+ void Finish();
+
+ // Advances the iterator to the next range of leaf cells.
+ // REQUIRES: !done()
+ void Next();
+
+ // If the iterator is already positioned at the beginning, returns false.
+ // Otherwise positions the iterator at the previous entry and returns true.
+ bool Prev();
+
+ // Positions the iterator at the first range with start_id() >= target.
+ // (Such an entry always exists as long as "target" is a valid leaf cell.
+ // Note that it is valid to access start_id() even when done() is true.)
+ //
+ // REQUIRES: target.is_leaf()
+ void Seek(S2CellId target);
+
+ // Returns true if no (s2cell_id, label) pairs intersect this range.
+ // Also returns true if done() is true.
+ bool is_empty() const;
+
+ // If advancing the iterator "n" times would leave it positioned on a
+ // valid range, does so and returns true. Otherwise leaves the iterator
+ // unmodified and returns false.
+ bool Advance(int n);
+
+ private:
+ // A special value used to indicate that the RangeIterator has not yet
+ // been initialized by calling Begin() or Seek().
+ std::vector<RangeNode>::const_iterator kUninitialized() const {
+ // Note that since the last element of range_nodes_ is a sentinel value,
+ // it_ will never legitimately be positioned at range_nodes_->end().
+ return range_nodes_->end();
+ }
+
+ friend class ContentsIterator;
+ const std::vector<RangeNode>* range_nodes_;
+ std::vector<RangeNode>::const_iterator it_;
+ };
+
+ // Like RangeIterator, but only visits leaf cell ranges that overlap at
+ // least one (cell_id, label) pair.
+ class NonEmptyRangeIterator : public RangeIterator {
+ public:
+ // Initializes a NonEmptyRangeIterator for the given S2CellIndex.
+ // The iterator is initially *unpositioned*; you must call a positioning
+ // method such as Begin() or Seek() before accessing its contents.
+ explicit NonEmptyRangeIterator(const S2CellIndex* index);
+
+ // Positions the iterator at the first non-empty range of leaf cells.
+ void Begin();
+
+ // Advances the iterator to the next non-empty range of leaf cells.
+ // REQUIRES: !done()
+ void Next();
+
+ // If the iterator is already positioned at the beginning, returns false.
+ // Otherwise positions the iterator at the previous entry and returns true.
+ bool Prev();
+
+ // Positions the iterator at the first non-empty range with
+ // start_id() >= target.
+ //
+ // REQUIRES: target.is_leaf()
+ void Seek(S2CellId target);
+ };
+
+ // An iterator that visits the (cell_id, label) pairs that cover a set of
+ // leaf cell ranges (see RangeIterator). Note that when multiple leaf cell
+ // ranges are visited, this class only guarantees that each result will be
+ // reported at least once, i.e. duplicate values may be suppressed. If you
+ // want duplicate values to be reported again, be sure to call Clear() first.
+ //
+ // [In particular, the implementation guarantees that when multiple leaf
+ // cell ranges are visited in monotonically increasing order, then each
+ // (cell_id, label) pair is reported exactly once.]
+ class ContentsIterator {
+ public:
+ // Default constructor; must be followed by a call to Init().
+ ContentsIterator();
+
+ // Convenience constructor that calls Init().
+ explicit ContentsIterator(const S2CellIndex* index);
+
+ // Initializes the iterator. Should be followed by a call to UnionWith()
+ // to visit the contents of each desired leaf cell range.
+ void Init(const S2CellIndex* index);
+
+ // Clears all state with respect to which range(s) have been visited.
+ void Clear();
+
+ // Positions the ContentsIterator at the first (cell_id, label) pair that
+ // covers the given leaf cell range. Note that when multiple leaf cell
+ // ranges are visited using the same ContentsIterator, duplicate values
+ // may be suppressed. If you don't want this behavior, call Clear() first.
+ void StartUnion(const RangeIterator& range);
+
+ // The S2CellId of the current (cell_id, label) pair.
+ // REQUIRES: !done()
+ S2CellId cell_id() const;
+
+ // The Label of the current (cell_id, label) pair.
+ // REQUIRES: !done()
+ Label label() const;
+
+ // Returns the current (cell_id, label) pair.
+ // REQUIRES: !done()
+ LabelledCell labelled_cell() const;
+
+ // Returns true if all (cell_id, label) pairs have been visited.
+ bool done() const;
+
+ // Advances the iterator to the next (cell_id, label) pair covered by the
+ // current leaf cell range.
+ // REQUIRES: !done()
+ void Next();
+
+ private:
+ // node_.label == kDoneContents indicates that done() is true.
+ void set_done() { node_.label = kDoneContents; }
+
+ // A pointer to the cell tree itself (owned by the S2CellIndex).
+ const std::vector<CellNode>* cell_tree_;
+
+ // The value of it.start_id() from the previous call to StartUnion().
+ // This is used to check whether these values are monotonically
+ // increasing.
+ S2CellId prev_start_id_;
+
+ // The maximum index within the cell_tree_ vector visited during the
+ // previous call to StartUnion(). This is used to eliminate duplicate
+ // values when StartUnion() is called multiple times.
+ int32 node_cutoff_;
+
+ // The maximum index within the cell_tree_ vector visited during the
+ // current call to StartUnion(). This is used to update node_cutoff_.
+ int32 next_node_cutoff_;
+
+ // A copy of the current node in the cell tree.
+ CellNode node_;
+ };
+
+ private:
+ friend class CellIterator;
+ friend class RangeIterator;
+ friend class ContentsIterator;
+
+ // A tree of (cell_id, label) pairs such that if X is an ancestor of Y, then
+ // X.cell_id contains Y.cell_id. The contents of a given range of leaf
+ // cells can be represented by pointing to a node of this tree.
+ std::vector<CellNode> cell_tree_;
+
+ // A RangeNode represents a range of leaf S2CellIds. The range starts at
+ // "start_id" (a leaf cell) and ends at the "start_id" field of the next
+ // RangeNode. "contents" points to the node of cell_tree_ representing the
+ // cells that overlap this range.
+ struct RangeNode {
+ S2CellId start_id; // First leaf cell contained by this range.
+ int32 contents; // Contents of this node (an index within cell_tree_).
+
+ RangeNode(S2CellId _start_id, int32 _contents)
+ : start_id(_start_id), contents(_contents) {
+ }
+
+ // Comparison operator needed for std::upper_bound().
+ friend bool operator<(S2CellId x, const RangeNode& y) {
+ return x < y.start_id;
+ }
+ };
+ // The last element of range_nodes_ is a sentinel value, which is necessary
+ // in order to represent the range covered by the previous element.
+ std::vector<RangeNode> range_nodes_;
+
+ S2CellIndex(const S2CellIndex&) = delete;
+ void operator=(const S2CellIndex&) = delete;
+};
+std::ostream& operator<<(std::ostream& os, S2CellIndex::LabelledCell x);
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline S2CellIndex::CellIterator::CellIterator(const S2CellIndex* index)
+ : cell_it_(index->cell_tree_.begin()),
+ cell_end_(index->cell_tree_.end()) {
+ S2_DCHECK(!index->range_nodes_.empty()) << "Call Build() first.";
+}
+
+inline S2CellId S2CellIndex::CellIterator::cell_id() const {
+ S2_DCHECK(!done());
+ return cell_it_->cell_id;
+}
+
+inline S2CellIndex::Label S2CellIndex::CellIterator::label() const {
+ S2_DCHECK(!done());
+ return cell_it_->label;
+}
+
+inline S2CellIndex::LabelledCell S2CellIndex::CellIterator::labelled_cell()
+ const {
+ S2_DCHECK(!done());
+ return LabelledCell(cell_it_->cell_id, cell_it_->label);
+}
+
+inline bool S2CellIndex::CellIterator::done() const {
+ return cell_it_ == cell_end_;
+}
+
+inline void S2CellIndex::CellIterator::Next() {
+ S2_DCHECK(!done());
+ ++cell_it_;
+}
+
+inline S2CellIndex::RangeIterator::RangeIterator(const S2CellIndex* index)
+ : range_nodes_(&index->range_nodes_), it_() {
+ S2_DCHECK(!range_nodes_->empty()) << "Call Build() first.";
+ if (google::DEBUG_MODE) it_ = kUninitialized(); // See done().
+}
+
+inline S2CellId S2CellIndex::RangeIterator::start_id() const {
+ return it_->start_id;
+}
+
+inline S2CellId S2CellIndex::RangeIterator::limit_id() const {
+ S2_DCHECK(!done());
+ return (it_ + 1)->start_id;
+}
+
+inline bool S2CellIndex::RangeIterator::done() const {
+ S2_DCHECK(it_ != kUninitialized()) << "Call Begin() or Seek() first.";
+
+ // Note that the last element of range_nodes_ is a sentinel value.
+ return it_ >= range_nodes_->end() - 1;
+}
+
+inline void S2CellIndex::RangeIterator::Begin() {
+ it_ = range_nodes_->begin();
+}
+
+inline void S2CellIndex::RangeIterator::Finish() {
+ // Note that the last element of range_nodes_ is a sentinel value.
+ it_ = range_nodes_->end() - 1;
+}
+
+inline void S2CellIndex::RangeIterator::Next() {
+ S2_DCHECK(!done());
+ ++it_;
+}
+
+inline bool S2CellIndex::RangeIterator::is_empty() const {
+ return it_->contents == kDoneContents;
+}
+
+inline bool S2CellIndex::RangeIterator::Advance(int n) {
+ // Note that the last element of range_nodes_ is a sentinel value.
+ if (n >= range_nodes_->end() - 1 - it_) return false;
+ it_ += n;
+ return true;
+}
+
+inline S2CellIndex::NonEmptyRangeIterator::NonEmptyRangeIterator(
+ const S2CellIndex* index)
+ : RangeIterator(index) {
+}
+
+inline void S2CellIndex::NonEmptyRangeIterator::Begin() {
+ RangeIterator::Begin();
+ while (is_empty() && !done()) RangeIterator::Next();
+}
+
+inline void S2CellIndex::NonEmptyRangeIterator::Next() {
+ do {
+ RangeIterator::Next();
+ } while (is_empty() && !done());
+}
+
+inline bool S2CellIndex::NonEmptyRangeIterator::Prev() {
+ while (RangeIterator::Prev()) {
+ if (!is_empty()) return true;
+ }
+ // Return the iterator to its original position.
+ if (is_empty() && !done()) Next();
+ return false;
+}
+
+inline void S2CellIndex::NonEmptyRangeIterator::Seek(S2CellId target) {
+ RangeIterator::Seek(target);
+ while (is_empty() && !done()) RangeIterator::Next();
+}
+
+inline bool S2CellIndex::RangeIterator::Prev() {
+ if (it_ == range_nodes_->begin()) return false;
+ --it_;
+ return true;
+}
+
+inline S2CellIndex::ContentsIterator::ContentsIterator()
+ : cell_tree_(nullptr) {
+}
+
+inline S2CellIndex::ContentsIterator::ContentsIterator(
+ const S2CellIndex* index) {
+ Init(index);
+}
+
+inline void S2CellIndex::ContentsIterator::Init(const S2CellIndex* index) {
+ cell_tree_ = &index->cell_tree_;
+ Clear();
+}
+
+inline void S2CellIndex::ContentsIterator::Clear() {
+ prev_start_id_ = S2CellId::None();
+ node_cutoff_ = -1;
+ next_node_cutoff_ = -1;
+ set_done();
+}
+
+inline S2CellId S2CellIndex::ContentsIterator::cell_id() const {
+ S2_DCHECK(!done());
+ return node_.cell_id;
+}
+
+inline S2CellIndex::Label S2CellIndex::ContentsIterator::label() const {
+ S2_DCHECK(!done());
+ return node_.label;
+}
+
+inline S2CellIndex::LabelledCell S2CellIndex::ContentsIterator::labelled_cell()
+ const {
+ S2_DCHECK(!done());
+ return LabelledCell(node_.cell_id, node_.label);
+}
+
+inline bool S2CellIndex::ContentsIterator::done() const {
+ return node_.label == kDoneContents;
+}
+
+inline void S2CellIndex::ContentsIterator::Next() {
+ S2_DCHECK(!done());
+ if (node_.parent <= node_cutoff_) {
+ // We have already processed this node and its ancestors.
+ node_cutoff_ = next_node_cutoff_;
+ set_done();
+ } else {
+ node_ = (*cell_tree_)[node_.parent];
+ }
+}
+
+inline int S2CellIndex::num_cells() const {
+ return cell_tree_.size();
+}
+
+inline void S2CellIndex::Add(S2CellId cell_id, Label label) {
+ S2_DCHECK(cell_id.is_valid());
+ S2_DCHECK_GE(label, 0);
+ cell_tree_.push_back(CellNode(cell_id, label, -1));
+}
+
+inline void S2CellIndex::Clear() {
+ cell_tree_.clear();
+ range_nodes_.clear();
+}
+
+inline bool S2CellIndex::VisitIntersectingCells(
+ const S2CellUnion& target, const CellVisitor& visitor) const {
+ if (target.empty()) return true;
+ auto it = target.begin();
+ ContentsIterator contents(this);
+ RangeIterator range(this);
+ range.Begin();
+ do {
+ if (range.limit_id() <= it->range_min()) {
+ range.Seek(it->range_min()); // Only seek when necessary.
+ }
+ for (; range.start_id() <= it->range_max(); range.Next()) {
+ for (contents.StartUnion(range); !contents.done(); contents.Next()) {
+ if (!visitor(contents.cell_id(), contents.label())) {
+ return false;
+ }
+ }
+ }
+ // Check whether the next target cell is also contained by the leaf cell
+ // range that we just processed. If so, we can skip over all such cells
+ // using binary search. This speeds up benchmarks by between 2x and 10x
+ // when the average number of intersecting cells is small (< 1).
+ if (++it != target.end() && it->range_max() < range.start_id()) {
+ // Skip to the first target cell that extends past the previous range.
+ it = std::lower_bound(it + 1, target.end(), range.start_id());
+ if ((it - 1)->range_max() >= range.start_id()) --it;
+ }
+ } while (it != target.end());
+ return true;
+}
+
+inline std::ostream& operator<<(std::ostream& os,
+ S2CellIndex::LabelledCell x) {
+ return os << "(" << x.cell_id << ", " << x.label << ")";
+}
+
+#endif // S2_S2CELL_INDEX_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2CELL_UNION_H_
+#define S2_S2CELL_UNION_H_
+
+#include <vector>
+
+#include "s2/base/commandlineflags.h"
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2region.h"
+#include "s2/third_party/absl/base/macros.h"
+
+class Decoder;
+class Encoder;
+class S1Angle;
+class S2Cap;
+class S2Cell;
+class S2LatLngRect;
+
+DECLARE_bool(s2debug);
+DECLARE_int32(s2cell_union_decode_max_num_cells);
+
+// An S2CellUnion is a region consisting of cells of various sizes. Typically
+// a cell union is used to approximate some other shape. There is a tradeoff
+// between the accuracy of the approximation and how many cells are used.
+// Unlike polygons, cells have a fixed hierarchical structure. This makes
+// them more suitable for optimizations based on preprocessing.
+//
+// An S2CellUnion is represented as a vector of sorted, non-overlapping
+// S2CellIds. By default the vector is also "normalized", meaning that groups
+// of 4 child cells have been replaced by their parent cell whenever possible.
+// S2CellUnions are not required to be normalized, but certain operations will
+// return different results if they are not (e.g., Contains(S2CellUnion).)
+//
+// S2CellUnion is movable and copyable.
+class S2CellUnion final : public S2Region {
+ public:
+ // Creates an empty cell union.
+ S2CellUnion() {}
+
+ // Constructs a cell union with the given S2CellIds, then calls Normalize()
+ // to sort them, remove duplicates, and merge cells when possible. (See
+ // FromNormalized if your vector is already normalized.)
+ //
+ // The argument is passed by value, so if you are passing a named variable
+ // and have no further use for it, consider using std::move().
+ //
+ // A cell union containing a single S2CellId may be constructed like this:
+ //
+ // S2CellUnion example({cell_id});
+ explicit S2CellUnion(std::vector<S2CellId> cell_ids);
+
+ // Convenience constructor that accepts a vector of uint64. Note that
+ // unlike the constructor above, this one makes a copy of "cell_ids".
+ explicit S2CellUnion(const std::vector<uint64>& cell_ids);
+
+ // Constructs a cell union for the whole sphere.
+ static S2CellUnion WholeSphere();
+
+ // Constructs a cell union from S2CellIds that have already been normalized
+ // (typically because they were extracted from another S2CellUnion).
+ //
+ // The argument is passed by value, so if you are passing a named variable
+ // and have no further use for it, consider using std::move().
+ //
+ // REQUIRES: "cell_ids" satisfies the requirements of IsNormalized().
+ static S2CellUnion FromNormalized(std::vector<S2CellId> cell_ids);
+
+ // Constructs a cell union from a vector of sorted, non-overlapping
+ // S2CellIds. Unlike the other constructors, FromVerbatim does not require
+ // that groups of 4 child cells have been replaced by their parent cell. In
+ // other words, "cell_ids" must satisfy the requirements of IsValid() but
+ // not necessarily IsNormalized().
+ //
+ // Note that if the cell union is not normalized, certain operations may
+ // return different results (e.g., Contains(S2CellUnion)).
+ //
+ // REQUIRES: "cell_ids" satisfies the requirements of IsValid().
+ static S2CellUnion FromVerbatim(std::vector<S2CellId> cell_ids);
+
+ // Constructs a cell union that corresponds to a continuous range of cell
+ // ids. The output is a normalized collection of cell ids that covers the
+ // leaf cells between "min_id" and "max_id" inclusive.
+ //
+ // REQUIRES: min_id.is_leaf(), max_id.is_leaf(), min_id <= max_id.
+ static S2CellUnion FromMinMax(S2CellId min_id, S2CellId max_id);
+
+ // Like FromMinMax() except that the union covers the range of leaf cells
+ // from "begin" (inclusive) to "end" (exclusive), as with Python ranges or
+ // STL iterator ranges. If (begin == end) the result is empty.
+ //
+ // REQUIRES: begin.is_leaf(), end.is_leaf(), begin <= end.
+ static S2CellUnion FromBeginEnd(S2CellId begin, S2CellId end);
+
+ // Init() methods corresponding to the constructors/factory methods above.
+ // TODO(ericv): Consider deprecating these methods in favor of using the
+ // constructors and move assignment operator.
+ void Init(std::vector<S2CellId> cell_ids);
+ void Init(const std::vector<uint64>& cell_ids);
+ void InitFromMinMax(S2CellId min_id, S2CellId max_id);
+ void InitFromBeginEnd(S2CellId begin, S2CellId end);
+
+ // Clears the contents of the cell union and minimizes memory usage.
+ void Clear();
+
+ // Gives ownership of the vector data to the client without copying, and
+ // clears the content of the cell union. The original data in cell_ids
+ // is lost if there was any.
+ std::vector<S2CellId> Release();
+
+ // Convenience methods for accessing the individual cell ids.
+ int num_cells() const { return static_cast<int>(cell_ids_.size()); }
+ S2CellId cell_id(int i) const { return cell_ids_[i]; }
+
+ // Vector-like methods for accessing the individual cell ids.
+ size_t size() const { return cell_ids_.size(); }
+ bool empty() const { return cell_ids_.empty(); }
+ S2CellId operator[](int i) const { return cell_ids_[i]; }
+
+ // Standard begin/end methods, to allow range-based for loops:
+ //
+ // for (S2CellId id : cell_union) { ... }
+ std::vector<S2CellId>::const_iterator begin() const;
+ std::vector<S2CellId>::const_iterator end() const;
+
+ // Direct access to the underlying vector for STL algorithms.
+ const std::vector<S2CellId>& cell_ids() const { return cell_ids_; }
+
+ // Returns true if the cell union is valid, meaning that the S2CellIds are
+ // valid, non-overlapping, and sorted in increasing order.
+ bool IsValid() const;
+
+ // Returns true if the cell union is normalized, meaning that it is
+ // satisfies IsValid() and that no four cells have a common parent.
+ // Certain operations such as Contains(S2CellUnion) will return a different
+ // result if the cell union is not normalized.
+ bool IsNormalized() const;
+
+ // Normalizes the cell union by discarding cells that are contained by other
+ // cells, replacing groups of 4 child cells by their parent cell whenever
+ // possible, and sorting all the cell ids in increasing order.
+ //
+ // Returns true if the number of cells was reduced.
+ // TODO(ericv): Change this method to return void.
+ bool Normalize();
+
+ // Replaces "output" with an expanded version of the cell union where any
+ // cells whose level is less than "min_level" or where (level - min_level)
+ // is not a multiple of "level_mod" are replaced by their children, until
+ // either both of these conditions are satisfied or the maximum level is
+ // reached.
+ //
+ // This method allows a covering generated by S2RegionCoverer using
+ // min_level() or level_mod() constraints to be stored as a normalized cell
+ // union (which allows various geometric computations to be done) and then
+ // converted back to the original list of cell ids that satisfies the
+ // desired constraints.
+ void Denormalize(int min_level, int level_mod,
+ std::vector<S2CellId>* output) const;
+
+ // If there are more than "excess" elements of the cell_ids() vector that
+ // are allocated but unused, reallocates the array to eliminate the excess
+ // space. This reduces memory usage when many cell unions need to be held
+ // in memory at once.
+ void Pack(int excess = 0);
+
+ // Returns true if the cell union contains the given cell id. Containment
+ // is defined with respect to regions, e.g. a cell contains its 4 children.
+ // This is a fast operation (logarithmic in the size of the cell union).
+ //
+ // CAVEAT: If you have constructed a non-normalized S2CellUnion using
+ // FromVerbatim, note that groups of 4 child cells are *not* considered to
+ // contain their parent cell. To get this behavior you must use one of the
+ // other constructors or call Normalize() explicitly.
+ bool Contains(S2CellId id) const;
+
+ // Returns true if the cell union intersects the given cell id.
+ // This is a fast operation (logarithmic in the size of the cell union).
+ bool Intersects(S2CellId id) const;
+
+ // Returns true if this cell union contains the given other cell union.
+ //
+ // CAVEAT: If you have constructed a non-normalized S2CellUnion using
+ // FromVerbatim, note that groups of 4 child cells are *not* considered to
+ // contain their parent cell. To get this behavior you must use one of the
+ // other constructors or call Normalize() explicitly.
+ bool Contains(const S2CellUnion& y) const;
+
+ // Returns true if this cell union intersects the given other cell union.
+ bool Intersects(const S2CellUnion& y) const;
+
+ // Returns the union of the two given cell unions.
+ S2CellUnion Union(const S2CellUnion& y) const;
+
+ // Returns the intersection of the two given cell unions.
+ S2CellUnion Intersection(const S2CellUnion& y) const;
+
+ // Specialized version of GetIntersection() that returns the intersection of
+ // a cell union with an S2CellId. This can be useful for splitting a cell
+ // union into pieces.
+ S2CellUnion Intersection(S2CellId id) const;
+
+ // Returns the difference of the two given cell unions.
+ S2CellUnion Difference(const S2CellUnion& y) const;
+
+ // Expands the cell union by adding a buffer of cells at "expand_level"
+ // around the union boundary.
+ //
+ // For each cell "c" in the union, we add all neighboring cells at level
+ // "expand_level" that are adjacent to "c". Note that there can be many
+ // such cells if "c" is large compared to "expand_level". If "c" is smaller
+ // than "expand_level", we first add the parent of "c" at "expand_level" and
+ // then add all the neighbors of that cell.
+ //
+ // Note that the size of the output is exponential in "expand_level". For
+ // example, if expand_level == 20 and the input has a cell at level 10,
+ // there will be on the order of 4000 adjacent cells in the output. For
+ // most applications the Expand(min_radius, max_level_diff) method below is
+ // easier to use.
+ void Expand(int expand_level);
+
+ // Expands the cell union such that it contains all points whose distance to
+ // the cell union is at most "min_radius", but do not use cells that are
+ // more than "max_level_diff" levels higher than the largest cell in the
+ // input. The second parameter controls the tradeoff between accuracy and
+ // output size when a large region is being expanded by a small amount
+ // (e.g. expanding Canada by 1km). For example, if max_level_diff == 4 the
+ // region will always be expanded by approximately 1/16 the width of its
+ // largest cell. Note that in the worst case, the number of cells in the
+ // output can be up to 4 * (1 + 2 ** max_level_diff) times larger than the
+ // number of cells in the input.
+ void Expand(S1Angle min_radius, int max_level_diff);
+
+ // The number of leaf cells covered by the union.
+ // This will be no more than 6*2^60 for the whole sphere.
+ uint64 LeafCellsCovered() const;
+
+ // Approximates this cell union's area in steradians by summing the average
+ // area of each contained cell's average area, using the AverageArea method
+ // from the S2Cell class. This is equivalent to the number of leaves covered,
+ // multiplied by the average area of a leaf. Note that AverageArea does not
+ // take into account distortion of cell, and thus may be off by up to a
+ // factor of up to 1.7.
+ //
+ // NOTE: Since this is proportional to LeafCellsCovered(), it is
+ // always better to use that function if all you care about is
+ // the relative average area between objects.
+ double AverageBasedArea() const;
+
+ // Calculates this cell union's area in steradians by summing the approximate
+ // area for each contained cell, using the ApproxArea method from the S2Cell
+ // class.
+ double ApproxArea() const;
+
+ // Calculates this cell union's area in steradians by summing the exact area
+ // for each contained cell, using the Exact method from the S2Cell class.
+ double ExactArea() const;
+
+ // Return true if two cell unions are identical.
+ friend bool operator==(const S2CellUnion& x, const S2CellUnion& y);
+
+ // Return true if two cell unions are different.
+ friend bool operator!=(const S2CellUnion& x, const S2CellUnion& y);
+
+ ////////////////////////////////////////////////////////////////////////
+ // S2Region interface (see s2region.h for details):
+
+ S2CellUnion* Clone() const override;
+ S2Cap GetCapBound() const override;
+ S2LatLngRect GetRectBound() const override;
+
+ // This is a fast operation (logarithmic in the size of the cell union).
+ bool Contains(const S2Cell& cell) const override;
+
+ // This is a fast operation (logarithmic in the size of the cell union).
+ bool MayIntersect(const S2Cell& cell) const override;
+
+ // The point 'p' does not need to be normalized.
+ // This is a fast operation (logarithmic in the size of the cell union).
+ bool Contains(const S2Point& p) const override;
+
+ // Appends a serialized representation of the S2CellUnion to "encoder".
+ //
+ // REQUIRES: "encoder" uses the default constructor, so that its buffer
+ // can be enlarged as necessary by calling Ensure(int).
+ void Encode(Encoder* const encoder) const;
+
+ // Decodes an S2CellUnion encoded with Encode(). Returns true on success.
+ bool Decode(Decoder* const decoder);
+
+ ////////////////////////////////////////////////////////////////////////
+ // Static methods intended for high-performance clients that prefer to
+ // manage their own storage.
+
+ // Like Normalize(), but works with a vector of S2CellIds.
+ // Equivalent to:
+ // *cell_ids = S2CellUnion(std::move(*cell_ids)).Release();
+ static bool Normalize(std::vector<S2CellId>* cell_ids);
+
+ // Like Denormalize(), but works with a vector of S2CellIds.
+ // REQUIRES: out != &in
+ static void Denormalize(const std::vector<S2CellId>& in,
+ int min_level, int level_mod,
+ std::vector<S2CellId>* out);
+
+ // Like GetIntersection(), but works directly with vectors of S2CellIds,
+ // Equivalent to:
+ //
+ // *out = S2CellUnion(x).Intersection(S2CellUnion(y)).Release()
+ //
+ // except that this method has slightly more relaxed normalization
+ // requirements: the input vectors may contain groups of 4 child cells that
+ // all have the same parent. (In a normalized S2CellUnion, such groups are
+ // always replaced by the parent cell.)
+ static void GetIntersection(const std::vector<S2CellId>& x,
+ const std::vector<S2CellId>& y,
+ std::vector<S2CellId>* out);
+
+ private:
+ friend class S2CellUnionTestPeer; // For creating invalid S2CellUnions.
+
+ // Internal constructor that does not check "cell_ids" for validity.
+ enum VerbatimFlag { VERBATIM };
+ S2CellUnion(std::vector<S2CellId> cell_ids, VerbatimFlag verbatim)
+ : cell_ids_(std::move(cell_ids)) {}
+
+ // Converts a vector of uint64 to a vector of S2CellIds.
+ static std::vector<S2CellId> ToS2CellIds(const std::vector<uint64>& ids);
+
+ std::vector<S2CellId> cell_ids_;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline S2CellUnion::S2CellUnion(std::vector<S2CellId> cell_ids)
+ : cell_ids_(std::move(cell_ids)) {
+ Normalize();
+}
+
+inline S2CellUnion S2CellUnion::FromNormalized(std::vector<S2CellId> cell_ids) {
+ S2CellUnion result(std::move(cell_ids), VERBATIM);
+ S2_DCHECK(result.IsNormalized());
+ return result;
+}
+
+inline S2CellUnion S2CellUnion::FromVerbatim(std::vector<S2CellId> cell_ids) {
+ S2CellUnion result(std::move(cell_ids), VERBATIM);
+ S2_DCHECK(!FLAGS_s2debug || result.IsValid());
+ return result;
+}
+
+inline void S2CellUnion::Init(std::vector<S2CellId> cell_ids) {
+ cell_ids_ = std::move(cell_ids);
+ Normalize();
+}
+
+inline void S2CellUnion::Clear() {
+ // swap() guarantees to reduce the RHS vector's size and capacity
+ // to zero (as opposed to clear(), shrink_to_fit() sequence).
+ std::vector<S2CellId>().swap(cell_ids_);
+}
+
+inline std::vector<S2CellId> S2CellUnion::Release() {
+ // vector's rvalue reference constructor does not necessarily leave
+ // moved-from value in empty state, so swap instead.
+ std::vector<S2CellId> cell_ids;
+ cell_ids_.swap(cell_ids);
+ return cell_ids;
+}
+
+inline std::vector<S2CellId>::const_iterator S2CellUnion::begin() const {
+ return cell_ids_.begin();
+}
+
+inline std::vector<S2CellId>::const_iterator S2CellUnion::end() const {
+ return cell_ids_.end();
+}
+
+#endif // S2_S2CELL_UNION_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// There are several notions of the "centroid" of a triangle. First, there
+// is the planar centroid, which is simply the centroid of the ordinary
+// (non-spherical) triangle defined by the three vertices. Second, there is
+// the surface centroid, which is defined as the intersection of the three
+// medians of the spherical triangle. It is possible to show that this
+// point is simply the planar centroid projected to the surface of the
+// sphere. Finally, there is the true centroid (mass centroid), which is
+// defined as the surface integral over the spherical triangle of (x,y,z)
+// divided by the triangle area. This is the point that the triangle would
+// rotate around if it was spinning in empty space.
+//
+// The best centroid for most purposes is the true centroid. Unlike the
+// planar and surface centroids, the true centroid behaves linearly as
+// regions are added or subtracted. That is, if you split a triangle into
+// pieces and compute the average of their centroids (weighted by triangle
+// area), the result equals the centroid of the original triangle. This is
+// not true of the other centroids.
+//
+// Also note that the surface centroid may be nowhere near the intuitive
+// "center" of a spherical triangle. For example, consider the triangle
+// with vertices A=(1,eps,0), B=(0,0,1), C=(-1,eps,0) (a quarter-sphere).
+// The surface centroid of this triangle is at S=(0, 2*eps, 1), which is
+// within a distance of 2*eps of the vertex B. Note that the median from A
+// (the segment connecting A to the midpoint of BC) passes through S, since
+// this is the shortest path connecting the two endpoints. On the other
+// hand, the true centroid is at M=(0, 0.5, 0.5), which when projected onto
+// the surface is a much more reasonable interpretation of the "center" of
+// this triangle.
+
+#ifndef S2_S2CENTROIDS_H_
+#define S2_S2CENTROIDS_H_
+
+#include "s2/s2point.h"
+
+namespace S2 {
+
+// Returns the centroid of the planar triangle ABC. This can be normalized to
+// unit length to obtain the "surface centroid" of the corresponding spherical
+// triangle, i.e. the intersection of the three medians. However, note that
+// for large spherical triangles the surface centroid may be nowhere near the
+// intuitive "center" (see example above).
+S2Point PlanarCentroid(const S2Point& a, const S2Point& b, const S2Point& c);
+
+// Returns the true centroid of the spherical triangle ABC multiplied by the
+// signed area of spherical triangle ABC. The reasons for multiplying by the
+// signed area are (1) this is the quantity that needs to be summed to compute
+// the centroid of a union or difference of triangles, and (2) it's actually
+// easier to calculate this way. All points must have unit length.
+//
+// Note that the result of this function is defined to be S2Point(0, 0, 0) if
+// the triangle is degenerate (and that this is intended behavior).
+S2Point TrueCentroid(const S2Point& a, const S2Point& b, const S2Point& c);
+
+// Returns the true centroid of the spherical geodesic edge AB multiplied by
+// the length of the edge AB. As with triangles, the true centroid of a
+// collection of edges may be computed simply by summing the result of this
+// method for each edge.
+//
+// Note that the planar centroid of a geodesic edge simply 0.5 * (a + b),
+// while the surface centroid is (a + b).Normalize(). However neither of
+// these values is appropriate for computing the centroid of a collection of
+// edges (such as a polyline).
+//
+// Also note that the result of this function is defined to be S2Point(0, 0, 0)
+// if the edge is degenerate (and that this is intended behavior).
+S2Point TrueCentroid(const S2Point& a, const S2Point& b);
+
+} // namespace S2
+
+#endif // S2_S2CENTROIDS_H_
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2CLOSEST_CELL_QUERY_H_
+#define S2_S2CLOSEST_CELL_QUERY_H_
+
+#include <vector>
+
+#include "s2/base/logging.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/s1angle.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s2cell.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2closest_cell_query_base.h"
+#include "s2/s2min_distance_targets.h"
+
+// S2ClosestCellQuery is a helper class for finding the closest cell(s) to a
+// given point, edge, S2Cell, S2CellUnion, or geometry collection. A typical
+// use case would be to add a collection of S2Cell coverings to an S2CellIndex
+// (representing a collection of original geometry), and then use
+// S2ClosestCellQuery to find all coverings that are within a given distance
+// of some target geometry (which could be represented exactly, or could also
+// be a covering). The distance to the original geometry corresponding to
+// each covering could then be measured more precisely if desired.
+//
+// For example, here is how to find all cells that are closer than
+// "distance_limit" to a given target point:
+//
+// S2ClosestCellQuery query(&cell_index);
+// query.mutable_options()->set_max_distance(distance_limit);
+// S2ClosestCellQuery::PointTarget target(target_point);
+// for (const auto& result : query.FindClosestCells(&target)) {
+// // result.distance() is the distance to the target.
+// // result.cell_id() is the indexed S2CellId.
+// // result.label() is the integer label associated with the S2CellId.
+// DoSomething(target_point, result);
+// }
+//
+// You can find either the k closest cells, or all cells within a given
+// radius, or both (i.e., the k closest cells up to a given maximum radius).
+// By default *all* cells are returned, so you should always specify either
+// max_results() or max_distance() or both. You can also restrict the results
+// to cells that intersect a given S2Region; for example:
+//
+// S2LatLngRect rect(...);
+// query.mutable_options()->set_region(&rect); // Does *not* take ownership.
+//
+// There is a FindClosestCell() convenience method that returns the closest
+// cell. However, if you only need to test whether the distance is above or
+// below a given threshold (e.g., 10 km), it is typically much faster to use
+// the IsDistanceLess() method instead. Unlike FindClosestCell(), this method
+// stops as soon as it can prove that the minimum distance is either above or
+// below the threshold. Example usage:
+//
+// if (query.IsDistanceLess(&target, limit_distance)) ...
+//
+// To find the closest cells to a query edge rather than a point, use:
+//
+// S2ClosestCellQuery::EdgeTarget target(v0, v1);
+// query.FindClosestCells(&target);
+//
+// Similarly you can find the closest cells to an S2Cell using an
+// S2ClosestCellQuery::CellTarget, you can find the closest cells to an
+// S2CellUnion using an S2ClosestCellQuery::CellUnionTarget, and you can find
+// the closest cells to an arbitrary collection of points, polylines, and
+// polygons by using an S2ClosestCellQuery::ShapeIndexTarget.
+//
+// The implementation is designed to be fast for both simple and complex
+// geometric objects.
+class S2ClosestCellQuery {
+ public:
+ // See S2ClosestCellQueryBase for full documentation.
+
+ // S2MinDistance is a thin wrapper around S1ChordAngle that implements the
+ // Distance concept required by S2ClosestCellQueryBase.
+ using Distance = S2MinDistance;
+ using Base = S2ClosestCellQueryBase<Distance>;
+
+ // Each "Result" object represents a closest (s2cell_id, label) pair. Here
+ // are its main methods (see S2ClosestCellQueryBase::Result for details):
+ //
+ // // The distance from the target to this point.
+ // S1ChordAngle distance() const;
+ //
+ // // The S2CellId itself.
+ // S2CellId cell_id() const;
+ //
+ // // The label associated with this S2CellId.
+ // const Label& label() const;
+ using Result = Base::Result;
+
+ // Options that control the set of cells returned. Note that by default
+ // *all* cells are returned, so you will always want to set either the
+ // max_results() option or the max_distance() option (or both).
+ class Options : public Base::Options {
+ public:
+ // See S2ClosestCellQueryBase::Options for the full set of options.
+
+ // Specifies that only cells whose distance to the target is less than
+ // "max_distance" should be returned.
+ //
+ // Note that cells whose distance is exactly equal to "max_distance" are
+ // not returned. Normally this doesn't matter, because distances are not
+ // computed exactly in the first place, but if such cells are needed then
+ // see set_inclusive_max_distance() below.
+ //
+ // DEFAULT: Distance::Infinity()
+ void set_max_distance(S1ChordAngle max_distance);
+
+ // Like set_max_distance(), except that cells whose distance is exactly
+ // equal to "max_distance" are also returned. Equivalent to calling
+ // set_max_distance(max_distance.Successor()).
+ void set_inclusive_max_distance(S1ChordAngle max_distance);
+
+ // Like set_inclusive_max_distance(), except that "max_distance" is also
+ // increased by the maximum error in the distance calculation. This
+ // ensures that all cells whose true distance is less than or equal to
+ // "max_distance" will be returned (along with some cells whose true
+ // distance is slightly greater).
+ //
+ // Algorithms that need to do exact distance comparisons can use this
+ // option to find a set of candidate cells that can then be filtered
+ // further (e.g., using s2pred::CompareDistance).
+ void set_conservative_max_distance(S1ChordAngle max_distance);
+
+ // Versions of set_max_distance that take an S1Angle argument. (Note that
+ // these functions require a conversion, and that the S1ChordAngle versions
+ // are preferred.)
+ void set_max_distance(S1Angle max_distance);
+ void set_inclusive_max_distance(S1Angle max_distance);
+ void set_conservative_max_distance(S1Angle max_distance);
+
+ // See S2ClosestCellQueryBase::Options for documentation.
+ using Base::Options::set_max_error; // S1Chordangle version
+ void set_max_error(S1Angle max_error); // S1Angle version
+
+ // Inherited options (see s2closest_cell_query_base.h for details):
+ using Base::Options::set_max_results;
+ using Base::Options::set_region;
+ using Base::Options::set_use_brute_force;
+ };
+
+ // "Target" represents the geometry to which the distance is measured.
+ // There are subtypes for measuring the distance to a point, an edge, an
+ // S2Cell, or an S2ShapeIndex (an arbitrary collection of geometry).
+ using Target = S2MinDistanceTarget;
+
+ // Target subtype that computes the closest distance to a point.
+ class PointTarget final : public S2MinDistancePointTarget {
+ public:
+ explicit PointTarget(const S2Point& point);
+ int max_brute_force_index_size() const override;
+ };
+
+ // Target subtype that computes the closest distance to an edge.
+ class EdgeTarget final : public S2MinDistanceEdgeTarget {
+ public:
+ explicit EdgeTarget(const S2Point& a, const S2Point& b);
+ int max_brute_force_index_size() const override;
+ };
+
+ // Target subtype that computes the closest distance to an S2Cell
+ // (including the interior of the cell).
+ class CellTarget final : public S2MinDistanceCellTarget {
+ public:
+ explicit CellTarget(const S2Cell& cell);
+ int max_brute_force_index_size() const override;
+ };
+
+ // Target subtype that computes the closest distance to an S2CellUnion.
+ class CellUnionTarget final : public S2MinDistanceCellUnionTarget {
+ public:
+ explicit CellUnionTarget(S2CellUnion cell_union);
+ int max_brute_force_index_size() const override;
+ };
+
+ // Target subtype that computes the closest distance to an S2ShapeIndex
+ // (an arbitrary collection of points, polylines, and/or polygons).
+ //
+ // By default, distances are measured to the boundary and interior of
+ // polygons in the S2ShapeIndex rather than to polygon boundaries only.
+ // If you wish to change this behavior, you may call
+ //
+ // target.set_include_interiors(false);
+ //
+ // (see S2MinDistanceShapeIndexTarget for details).
+ class ShapeIndexTarget final : public S2MinDistanceShapeIndexTarget {
+ public:
+ explicit ShapeIndexTarget(const S2ShapeIndex* index);
+ int max_brute_force_index_size() const override;
+ };
+
+ // Convenience constructor that calls Init(). Options may be specified here
+ // or changed at any time using the mutable_options() accessor method.
+ //
+ // REQUIRES: "index" must persist for the lifetime of this object.
+ // REQUIRES: ReInit() must be called if "index" is modified.
+ explicit S2ClosestCellQuery(const S2CellIndex* index,
+ const Options& options = Options());
+
+ // Default constructor; requires Init() to be called.
+ S2ClosestCellQuery();
+ ~S2ClosestCellQuery();
+
+ // Initializes the query. Options may be specified here or changed at any
+ // time using the mutable_options() accessor method.
+ //
+ // REQUIRES: "index" must persist for the lifetime of this object.
+ // REQUIRES: ReInit() must be called if "index" is modified.
+ void Init(const S2CellIndex* index, const Options& options = Options());
+
+ // Reinitializes the query. This method must be called if the underlying
+ // S2CellIndex is modified (by calling Clear() and Build() again).
+ void ReInit();
+
+ // Returns a reference to the underlying S2CellIndex.
+ const S2CellIndex& index() const;
+
+ // Returns the query options. Options can be modified between queries.
+ const Options& options() const;
+ Options* mutable_options();
+
+ // Returns the closest cells to the given target that satisfy the current
+ // options. This method may be called multiple times.
+ std::vector<Result> FindClosestCells(Target* target);
+
+ // This version can be more efficient when this method is called many times,
+ // since it does not require allocating a new vector on each call.
+ void FindClosestCells(Target* target, std::vector<Result>* results);
+
+ //////////////////////// Convenience Methods ////////////////////////
+
+ // Returns the closest cell to the target. If no cell satisfies the search
+ // criteria, then the Result object will have distance == Infinity() and
+ // is_empty() == true.
+ Result FindClosestCell(Target* target);
+
+ // Returns the minimum distance to the target. If the index or target is
+ // empty, returns S1ChordAngle::Infinity().
+ //
+ // Use IsDistanceLess() if you only want to compare the distance against a
+ // threshold value, since it is often much faster.
+ S1ChordAngle GetDistance(Target* target);
+
+ // Returns true if the distance to "target" is less than "limit".
+ //
+ // This method is usually much faster than GetDistance(), since it is much
+ // less work to determine whether the minimum distance is above or below a
+ // threshold than it is to calculate the actual minimum distance.
+ bool IsDistanceLess(Target* target, S1ChordAngle limit);
+
+ // Like IsDistanceLess(), but also returns true if the distance to "target"
+ // is exactly equal to "limit".
+ bool IsDistanceLessOrEqual(Target* target, S1ChordAngle limit);
+
+ // Like IsDistanceLessOrEqual(), except that "limit" is increased by the
+ // maximum error in the distance calculation. This ensures that this
+ // function returns true whenever the true, exact distance is less than
+ // or equal to "limit".
+ bool IsConservativeDistanceLessOrEqual(Target* target, S1ChordAngle limit);
+
+ private:
+ Options options_;
+ Base base_;
+
+ S2ClosestCellQuery(const S2ClosestCellQuery&) = delete;
+ void operator=(const S2ClosestCellQuery&) = delete;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline void S2ClosestCellQuery::Options::set_max_distance(
+ S1ChordAngle max_distance) {
+ Base::Options::set_max_distance(Distance(max_distance));
+}
+
+inline void S2ClosestCellQuery::Options::set_max_distance(
+ S1Angle max_distance) {
+ Base::Options::set_max_distance(Distance(max_distance));
+}
+
+inline void S2ClosestCellQuery::Options::set_inclusive_max_distance(
+ S1ChordAngle max_distance) {
+ set_max_distance(max_distance.Successor());
+}
+
+inline void S2ClosestCellQuery::Options::set_inclusive_max_distance(
+ S1Angle max_distance) {
+ set_inclusive_max_distance(S1ChordAngle(max_distance));
+}
+
+inline void S2ClosestCellQuery::Options::set_max_error(S1Angle max_error) {
+ Base::Options::set_max_error(S1ChordAngle(max_error));
+}
+
+inline S2ClosestCellQuery::PointTarget::PointTarget(const S2Point& point)
+ : S2MinDistancePointTarget(point) {
+}
+
+inline S2ClosestCellQuery::EdgeTarget::EdgeTarget(const S2Point& a,
+ const S2Point& b)
+ : S2MinDistanceEdgeTarget(a, b) {
+}
+
+inline S2ClosestCellQuery::CellTarget::CellTarget(const S2Cell& cell)
+ : S2MinDistanceCellTarget(cell) {
+}
+
+inline S2ClosestCellQuery::CellUnionTarget::CellUnionTarget(
+ S2CellUnion cell_union)
+ : S2MinDistanceCellUnionTarget(std::move(cell_union)) {
+}
+
+inline S2ClosestCellQuery::ShapeIndexTarget::ShapeIndexTarget(
+ const S2ShapeIndex* index)
+ : S2MinDistanceShapeIndexTarget(index) {
+}
+
+inline S2ClosestCellQuery::S2ClosestCellQuery(const S2CellIndex* index,
+ const Options& options) {
+ Init(index, options);
+}
+
+inline void S2ClosestCellQuery::Init(const S2CellIndex* index,
+ const Options& options) {
+ options_ = options;
+ base_.Init(index);
+}
+
+inline void S2ClosestCellQuery::ReInit() {
+ base_.ReInit();
+}
+
+inline const S2CellIndex& S2ClosestCellQuery::index() const {
+ return base_.index();
+}
+
+inline const S2ClosestCellQuery::Options& S2ClosestCellQuery::options() const {
+ return options_;
+}
+
+inline S2ClosestCellQuery::Options* S2ClosestCellQuery::mutable_options() {
+ return &options_;
+}
+
+inline std::vector<S2ClosestCellQuery::Result>
+S2ClosestCellQuery::FindClosestCells(Target* target) {
+ return base_.FindClosestCells(target, options_);
+}
+
+inline void S2ClosestCellQuery::FindClosestCells(Target* target,
+ std::vector<Result>* results) {
+ base_.FindClosestCells(target, options_, results);
+}
+
+inline S2ClosestCellQuery::Result S2ClosestCellQuery::FindClosestCell(
+ Target* target) {
+ static_assert(sizeof(Options) <= 32, "Consider not copying Options here");
+ Options tmp_options = options_;
+ tmp_options.set_max_results(1);
+ return base_.FindClosestCell(target, tmp_options);
+}
+
+inline S1ChordAngle S2ClosestCellQuery::GetDistance(Target* target) {
+ return FindClosestCell(target).distance();
+}
+
+#endif // S2_S2CLOSEST_CELL_QUERY_H_
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// See S2ClosestCellQueryBase (defined below) for an overview.
+
+#ifndef S2_S2CLOSEST_CELL_QUERY_BASE_H_
+#define S2_S2CLOSEST_CELL_QUERY_BASE_H_
+
+#include <vector>
+
+#include "s2/base/logging.h"
+#include "s2/util/gtl/btree_set.h"
+#include "s2/third_party/absl/container/inlined_vector.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s2cap.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2cell_index.h"
+#include "s2/s2cell_union.h"
+#include "s2/s2distance_target.h"
+#include "s2/s2region_coverer.h"
+#include "s2/util/gtl/dense_hash_set.h"
+#include "s2/util/hash/mix.h"
+
+// S2ClosestCellQueryBase is a templatized class for finding the closest
+// (cell_id, label) pairs in an S2CellIndex to a given target. It is not
+// intended to be used directly, but rather to serve as the implementation of
+// various specialized classes with more convenient APIs (such as
+// S2ClosestCellQuery). It is flexible enough so that it can be adapted to
+// compute maximum distances and even potentially Hausdorff distances.
+//
+// By using the appropriate options, this class can answer questions such as:
+//
+// - Find the minimum distance between a cell collection A and a target B.
+// - Find all cells in collection A that are within a distance D of target B.
+// - Find the k cells of collection A that are closest to a given point P.
+//
+// The target is any class that implements the S2DistanceTarget interface.
+// There are predefined targets for points, edges, S2Cells, S2CellUnions, and
+// S2ShapeIndexes (arbitrary collctions of points, polylines, and polygons).
+//
+// The Distance template argument is used to represent distances. Usually it
+// is a thin wrapper around S1ChordAngle, but another distance type may be
+// used as long as it implements the Distance concept described in
+// s2distance_targets.h. For example this can be used to measure maximum
+// distances, to get more accuracy, or to measure non-spheroidal distances.
+template <class Distance>
+class S2ClosestCellQueryBase {
+ public:
+ using Delta = typename Distance::Delta;
+ using Label = S2CellIndex::Label;
+ using LabelledCell = S2CellIndex::LabelledCell;
+
+ // Options that control the set of cells returned. Note that by default
+ // *all* cells are returned, so you will always want to set either the
+ // max_results() option or the max_distance() option (or both).
+ //
+ // This class is also available as S2ClosestCellQueryBase<Data>::Options.
+ //
+ // The Distance template argument is described below.
+ class Options {
+ public:
+ Options();
+
+ // Specifies that at most "max_results" cells should be returned.
+ //
+ // REQUIRES: max_results >= 1
+ // DEFAULT: kMaxMaxResults
+ int max_results() const;
+ void set_max_results(int max_results);
+ static constexpr int kMaxMaxResults = std::numeric_limits<int>::max();
+
+ // Specifies that only cells whose distance to the target is less than
+ // "max_distance" should be returned.
+ //
+ // Note that cells whose distance is exactly equal to "max_distance" are
+ // not returned. In most cases this doesn't matter (since distances are
+ // not computed exactly in the first place), but if such cells are needed
+ // then you can retrieve them by specifying "max_distance" as the next
+ // largest representable Distance. For example, if Distance is an
+ // S1ChordAngle then you can specify max_distance.Successor().
+ //
+ // DEFAULT: Distance::Infinity()
+ Distance max_distance() const;
+ void set_max_distance(Distance max_distance);
+
+ // Specifies that cells up to max_error() further away than the true
+ // closest cells may be substituted in the result set, as long as such
+ // cells satisfy all the remaining search criteria (such as max_distance).
+ // This option only has an effect if max_results() is also specified;
+ // otherwise all cells closer than max_distance() will always be returned.
+ //
+ // Note that this does not affect how the distance between cells is
+ // computed; it simply gives the algorithm permission to stop the search
+ // early as soon as the best possible improvement drops below max_error().
+ //
+ // This can be used to implement distance predicates efficiently. For
+ // example, to determine whether the minimum distance is less than D, the
+ // IsDistanceLess() method sets max_results() == 1 and max_distance() ==
+ // max_error() == D. This causes the algorithm to terminate as soon as it
+ // finds any cell whose distance is less than D, rather than continuing to
+ // search for a cell that is even closer.
+ //
+ // DEFAULT: Distance::Delta::Zero()
+ Delta max_error() const;
+ void set_max_error(Delta max_error);
+
+ // Specifies that cells must intersect the given S2Region. "region" is
+ // owned by the caller and must persist during the lifetime of this
+ // object. The value may be changed between calls to FindClosestPoints(),
+ // or reset by calling set_region(nullptr).
+ //
+ // Note that if you want to set the region to a disc around a target
+ // point, it is faster to use a PointTarget with set_max_distance()
+ // instead. You can also call both methods, e.g. to set a maximum
+ // distance and also require that cells lie within a given rectangle.
+ const S2Region* region() const;
+ void set_region(const S2Region* region);
+
+ // Specifies that distances should be computed by examining every cell
+ // rather than using the S2ShapeIndex. This is useful for testing,
+ // benchmarking, and debugging.
+ //
+ // DEFAULT: false
+ bool use_brute_force() const;
+ void set_use_brute_force(bool use_brute_force);
+
+ private:
+ Distance max_distance_ = Distance::Infinity();
+ Delta max_error_ = Delta::Zero();
+ const S2Region* region_ = nullptr;
+ int max_results_ = kMaxMaxResults;
+ bool use_brute_force_ = false;
+ };
+
+ // The Target class represents the geometry to which the distance is
+ // measured. For example, there can be subtypes for measuring the distance
+ // to a point, an edge, or to an S2ShapeIndex (an arbitrary collection of
+ // geometry).
+ //
+ // Implementations do *not* need to be thread-safe. They may cache data or
+ // allocate temporary data structures in order to improve performance.
+ using Target = S2DistanceTarget<Distance>;
+
+ // Each "Result" object represents a closest (cell_id, label) pair.
+ class Result {
+ public:
+ // The default constructor yields an empty result, with a distance() of
+ // Infinity() and invalid cell_id() and label() values.
+ Result() : distance_(Distance::Infinity()), cell_id_(S2CellId::None()),
+ label_(-1) {}
+
+ // Constructs a Result object for the given (cell_id, label) pair.
+ Result(Distance distance, S2CellId cell_id, Label label)
+ : distance_(distance), cell_id_(cell_id), label_(label) {}
+
+ // The distance from the target to this cell.
+ Distance distance() const { return distance_; }
+
+ // The cell itself.
+ S2CellId cell_id() const { return cell_id_; }
+
+ // The label associated with this S2CellId.
+ Label label() const { return label_; }
+
+ // Returns true if this Result object does not refer to any cell.
+ // (The only case where an empty Result is returned is when the
+ // FindClosestCell() method does not find any cells that meet the
+ // specified criteria.)
+ bool is_empty() const { return cell_id_ == S2CellId::None(); }
+
+ // Returns true if two Result objects are identical.
+ friend bool operator==(const Result& x, const Result& y) {
+ return (x.distance_ == y.distance_ &&
+ x.cell_id_ == y.cell_id_ &&
+ x.label_ == y.label_);
+ }
+
+ // Compares two Result objects first by distance, then by cell_id and
+ // finally by label.
+ friend bool operator<(const Result& x, const Result& y) {
+ if (x.distance_ < y.distance_) return true;
+ if (y.distance_ < x.distance_) return false;
+ if (x.cell_id_ < y.cell_id_) return true;
+ if (y.cell_id_ < x.cell_id_) return false;
+ return x.label_ < y.label_;
+ }
+
+ // Indicates that linear rather than binary search should be used when this
+ // type is used as the key in gtl::btree data structures.
+ using goog_btree_prefer_linear_node_search = std::true_type;
+
+ private:
+ Distance distance_;
+ S2CellId cell_id_;
+ Label label_;
+ };
+
+ // The minimum number of ranges that a cell must contain to enqueue it
+ // rather than processing its contents immediately.
+ static constexpr int kMinRangesToEnqueue = 6;
+
+ // Default constructor; requires Init() to be called.
+ S2ClosestCellQueryBase();
+ ~S2ClosestCellQueryBase();
+
+ // Convenience constructor that calls Init().
+ explicit S2ClosestCellQueryBase(const S2CellIndex* index);
+
+ // S2ClosestCellQueryBase is not copyable.
+ S2ClosestCellQueryBase(const S2ClosestCellQueryBase&) = delete;
+ void operator=(const S2ClosestCellQueryBase&) = delete;
+
+ // Initializes the query.
+ // REQUIRES: ReInit() must be called if "index" is modified.
+ void Init(const S2CellIndex* index);
+
+ // Reinitializes the query. This method must be called whenever the
+ // underlying index is modified.
+ void ReInit();
+
+ // Return a reference to the underlying S2CellIndex.
+ const S2CellIndex& index() const;
+
+ // Returns the closest (cell_id, label) pairs to the given target that
+ // satisfy the given options. This method may be called multiple times.
+ std::vector<Result> FindClosestCells(Target* target, const Options& options);
+
+ // This version can be more efficient when this method is called many times,
+ // since it does not require allocating a new vector on each call.
+ void FindClosestCells(Target* target, const Options& options,
+ std::vector<Result>* results);
+
+ // Convenience method that returns exactly one (cell_id, label) pair. If no
+ // cells satisfy the given search criteria, then a Result with
+ // distance() == Infinity() and is_empty() == true is returned.
+ //
+ // REQUIRES: options.max_results() == 1
+ Result FindClosestCell(Target* target, const Options& options);
+
+ private:
+ using CellIterator = S2CellIndex::CellIterator;
+ using ContentsIterator = S2CellIndex::ContentsIterator;
+ using NonEmptyRangeIterator = S2CellIndex::NonEmptyRangeIterator;
+ using RangeIterator = S2CellIndex::RangeIterator;
+
+ const Options& options() const { return *options_; }
+ void FindClosestCellsInternal(Target* target, const Options& options);
+ void FindClosestCellsBruteForce();
+ void FindClosestCellsOptimized();
+ void InitQueue();
+ void InitCovering();
+ void AddInitialRange(S2CellId first_id, S2CellId last_id);
+ void MaybeAddResult(S2CellId cell_id, Label label);
+ bool ProcessOrEnqueue(S2CellId id, NonEmptyRangeIterator* iter, bool seek);
+ void AddRange(const RangeIterator& range);
+
+ const S2CellIndex* index_;
+ const Options* options_;
+ Target* target_;
+
+ // True if max_error() must be subtracted from priority queue cell distances
+ // in order to ensure that such distances are measured conservatively. This
+ // is true only if the target takes advantage of max_error() in order to
+ // return faster results, and 0 < max_error() < distance_limit_.
+ bool use_conservative_cell_distance_;
+
+ // For the optimized algorithm we precompute the top-level S2CellIds that
+ // will be added to the priority queue. There can be at most 6 of these
+ // cells. Essentially this is just a covering of the indexed cells.
+ std::vector<S2CellId> index_covering_;
+
+ // The distance beyond which we can safely ignore further candidate cells.
+ // (Candidates that are exactly at the limit are ignored; this is more
+ // efficient for UpdateMinDistance() and should not affect clients since
+ // distance measurements have a small amount of error anyway.)
+ //
+ // Initially this is the same as the maximum distance specified by the user,
+ // but it can also be updated by the algorithm (see MaybeAddResult).
+ Distance distance_limit_;
+
+ // The current result set is stored in one of three ways:
+ //
+ // - If max_results() == 1, the best result is kept in result_singleton_.
+ //
+ // - If max_results() == kMaxMaxResults, results are appended to
+ // result_vector_ and sorted/uniqued at the end.
+ //
+ // - Otherwise results are kept in a btree_set so that we can progressively
+ // reduce the distance limit once max_results() results have been found.
+ // (A priority queue is not sufficient because we need to be able to
+ // check whether a candidate cell is already in the result set.)
+ //
+ // TODO(ericv): Check whether it would be faster to use avoid_duplicates_
+ // when result_set_ is used so that we could use a priority queue instead.
+ Result result_singleton_;
+ std::vector<Result> result_vector_;
+ gtl::btree_set<Result> result_set_;
+
+ // When the results are stored in a btree_set (see above), usually
+ // duplicates can be removed simply by inserting candidate cells in the
+ // current result set. However this is not true if Options::max_error() > 0
+ // and the Target subtype takes advantage of this by returning suboptimal
+ // distances. This is because when UpdateMinDistance() is called with
+ // different "min_dist" parameters (i.e., the distance to beat), the
+ // implementation may return a different distance for the same cell. Since
+ // the btree_set is keyed by (distance, cell_id, label) this can create
+ // duplicate results.
+ //
+ // The flag below is true when duplicates must be avoided explicitly. This
+ // is achieved by maintaining a separate set keyed by (cell_id, label) only,
+ // and checking whether each edge is in that set before computing the
+ // distance to it.
+ //
+ // TODO(ericv): Check whether it is faster to avoid duplicates by default
+ // (even when Options::max_results() == 1), rather than just when we need to.
+ bool avoid_duplicates_;
+ struct LabelledCellHash {
+ size_t operator()(LabelledCell x) const {
+ HashMix mix(x.cell_id.id());
+ mix.Mix(x.label);
+ return mix.get();
+ }
+ };
+ gtl::dense_hash_set<LabelledCell, LabelledCellHash> tested_cells_;
+
+ // The algorithm maintains a priority queue of unprocessed S2CellIds, sorted
+ // in increasing order of distance from the target.
+ struct QueueEntry {
+ // A lower bound on the distance from the target to "id". This is the key
+ // of the priority queue.
+ Distance distance;
+
+ // The cell being queued.
+ S2CellId id;
+
+ QueueEntry(Distance _distance, S2CellId _id)
+ : distance(_distance), id(_id) {}
+
+ bool operator<(const QueueEntry& other) const {
+ // The priority queue returns the largest elements first, so we want the
+ // "largest" entry to have the smallest distance.
+ return other.distance < distance;
+ }
+ };
+ using CellQueue =
+ std::priority_queue<QueueEntry, absl::InlinedVector<QueueEntry, 16>>;
+ CellQueue queue_;
+
+ // Used to iterate over the contents of an S2CellIndex range. It is defined
+ // here to take advantage of the fact that when multiple ranges are visited
+ // in increasing order, duplicates can automatically be eliminated.
+ S2CellIndex::ContentsIterator contents_it_;
+
+ // Temporaries, defined here to avoid multiple allocations / initializations.
+
+ std::vector<S2CellId> max_distance_covering_;
+ std::vector<S2CellId> intersection_with_max_distance_;
+ const LabelledCell* tmp_range_data_[kMinRangesToEnqueue - 1];
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+template <class Distance>
+inline S2ClosestCellQueryBase<Distance>::Options::Options() {
+}
+
+template <class Distance>
+inline int S2ClosestCellQueryBase<Distance>::Options::max_results() const {
+ return max_results_;
+}
+
+template <class Distance>
+inline void S2ClosestCellQueryBase<Distance>::Options::set_max_results(
+ int max_results) {
+ S2_DCHECK_GE(max_results, 1);
+ max_results_ = max_results;
+}
+
+template <class Distance>
+inline Distance S2ClosestCellQueryBase<Distance>::Options::max_distance()
+ const {
+ return max_distance_;
+}
+
+template <class Distance>
+inline void S2ClosestCellQueryBase<Distance>::Options::set_max_distance(
+ Distance max_distance) {
+ max_distance_ = max_distance;
+}
+
+template <class Distance>
+inline typename Distance::Delta
+S2ClosestCellQueryBase<Distance>::Options::max_error() const {
+ return max_error_;
+}
+
+template <class Distance>
+inline void S2ClosestCellQueryBase<Distance>::Options::set_max_error(
+ Delta max_error) {
+ max_error_ = max_error;
+}
+
+template <class Distance>
+inline const S2Region* S2ClosestCellQueryBase<Distance>::Options::region()
+ const {
+ return region_;
+}
+
+template <class Distance>
+inline void S2ClosestCellQueryBase<Distance>::Options::set_region(
+ const S2Region* region) {
+ region_ = region;
+}
+
+template <class Distance>
+inline bool S2ClosestCellQueryBase<Distance>::Options::use_brute_force() const {
+ return use_brute_force_;
+}
+
+template <class Distance>
+inline void S2ClosestCellQueryBase<Distance>::Options::set_use_brute_force(
+ bool use_brute_force) {
+ use_brute_force_ = use_brute_force;
+}
+
+template <class Distance>
+S2ClosestCellQueryBase<Distance>::S2ClosestCellQueryBase()
+ : tested_cells_(1) /* expected_max_elements*/ {
+ tested_cells_.set_empty_key(LabelledCell(S2CellId::None(), -1));
+}
+
+template <class Distance>
+S2ClosestCellQueryBase<Distance>::~S2ClosestCellQueryBase() {
+ // Prevent inline destructor bloat by providing a definition.
+}
+
+template <class Distance>
+inline S2ClosestCellQueryBase<Distance>::S2ClosestCellQueryBase(
+ const S2CellIndex* index) : S2ClosestCellQueryBase() {
+ Init(index);
+}
+
+template <class Distance>
+void S2ClosestCellQueryBase<Distance>::Init(
+ const S2CellIndex* index) {
+ index_ = index;
+ contents_it_.Init(index);
+ ReInit();
+}
+
+template <class Distance>
+void S2ClosestCellQueryBase<Distance>::ReInit() {
+ index_covering_.clear();
+}
+
+template <class Distance>
+inline const S2CellIndex&
+S2ClosestCellQueryBase<Distance>::index() const {
+ return *index_;
+}
+
+template <class Distance>
+inline std::vector<typename S2ClosestCellQueryBase<Distance>::Result>
+S2ClosestCellQueryBase<Distance>::FindClosestCells(
+ Target* target, const Options& options) {
+ std::vector<Result> results;
+ FindClosestCells(target, options, &results);
+ return results;
+}
+
+template <class Distance>
+typename S2ClosestCellQueryBase<Distance>::Result
+S2ClosestCellQueryBase<Distance>::FindClosestCell(
+ Target* target, const Options& options) {
+ S2_DCHECK_EQ(options.max_results(), 1);
+ FindClosestCellsInternal(target, options);
+ return result_singleton_;
+}
+
+template <class Distance>
+void S2ClosestCellQueryBase<Distance>::FindClosestCells(
+ Target* target, const Options& options, std::vector<Result>* results) {
+ FindClosestCellsInternal(target, options);
+ results->clear();
+ if (options.max_results() == 1) {
+ if (!result_singleton_.is_empty()) {
+ results->push_back(result_singleton_);
+ }
+ } else if (options.max_results() == Options::kMaxMaxResults) {
+ std::sort(result_vector_.begin(), result_vector_.end());
+ std::unique_copy(result_vector_.begin(), result_vector_.end(),
+ std::back_inserter(*results));
+ result_vector_.clear();
+ } else {
+ results->assign(result_set_.begin(), result_set_.end());
+ result_set_.clear();
+ }
+}
+
+template <class Distance>
+void S2ClosestCellQueryBase<Distance>::FindClosestCellsInternal(
+ Target* target, const Options& options) {
+ target_ = target;
+ options_ = &options;
+
+ tested_cells_.clear();
+ contents_it_.Clear();
+ distance_limit_ = options.max_distance();
+ result_singleton_ = Result();
+ S2_DCHECK(result_vector_.empty());
+ S2_DCHECK(result_set_.empty());
+ S2_DCHECK_GE(target->max_brute_force_index_size(), 0);
+ if (distance_limit_ == Distance::Zero()) return;
+
+ if (options.max_results() == Options::kMaxMaxResults &&
+ options.max_distance() == Distance::Infinity() &&
+ options.region() == nullptr) {
+ S2_LOG(WARNING) << "Returning all cells "
+ "(max_results/max_distance/region not set)";
+ }
+
+ // If max_error() > 0 and the target takes advantage of this, then we may
+ // need to adjust the distance estimates to the priority queue cells to
+ // ensure that they are always a lower bound on the true distance. For
+ // example, suppose max_distance == 100, max_error == 30, and we compute the
+ // distance to the target from some cell C0 as d(C0) == 80. Then because
+ // the target takes advantage of max_error(), the true distance could be as
+ // low as 50. In order not to miss edges contained by such cells, we need
+ // to subtract max_error() from the distance estimates. This behavior is
+ // controlled by the use_conservative_cell_distance_ flag.
+ //
+ // However there is one important case where this adjustment is not
+ // necessary, namely when max_distance() < max_error(). This is because
+ // max_error() only affects the algorithm once at least max_edges() edges
+ // have been found that satisfy the given distance limit. At that point,
+ // max_error() is subtracted from distance_limit_ in order to ensure that
+ // any further matches are closer by at least that amount. But when
+ // max_distance() < max_error(), this reduces the distance limit to 0,
+ // i.e. all remaining candidate cells and edges can safely be discarded.
+ // (Note that this is how IsDistanceLess() and friends are implemented.)
+ //
+ // Note that Distance::Delta only supports operator==.
+ bool target_uses_max_error = (!(options.max_error() == Delta::Zero()) &&
+ target_->set_max_error(options.max_error()));
+
+ // Note that we can't compare max_error() and distance_limit_ directly
+ // because one is a Delta and one is a Distance. Instead we subtract them.
+ use_conservative_cell_distance_ = target_uses_max_error &&
+ (distance_limit_ == Distance::Infinity() ||
+ Distance::Zero() < distance_limit_ - options.max_error());
+
+ // Use the brute force algorithm if the index is small enough.
+ if (options.use_brute_force() ||
+ index_->num_cells() <= target_->max_brute_force_index_size()) {
+ avoid_duplicates_ = false;
+ FindClosestCellsBruteForce();
+ } else {
+ // If the target takes advantage of max_error() then we need to avoid
+ // duplicate edges explicitly. (Otherwise it happens automatically.)
+ avoid_duplicates_ = (target_uses_max_error && options.max_results() > 1);
+ FindClosestCellsOptimized();
+ }
+}
+
+template <class Distance>
+void S2ClosestCellQueryBase<Distance>::FindClosestCellsBruteForce() {
+ for (CellIterator it(index_); !it.done(); it.Next()) {
+ MaybeAddResult(it.cell_id(), it.label());
+ }
+}
+
+template <class Distance>
+void S2ClosestCellQueryBase<Distance>::FindClosestCellsOptimized() {
+ InitQueue();
+ while (!queue_.empty()) {
+ // We need to copy the top entry before removing it, and we need to remove
+ // it before adding any new entries to the queue.
+ QueueEntry entry = queue_.top();
+ queue_.pop();
+ // Work around weird parse error in gcc 4.9 by using a local variable for
+ // entry.distance.
+ Distance distance = entry.distance;
+ if (!(distance < distance_limit_)) {
+ queue_ = CellQueue(); // Clear any remaining entries.
+ break;
+ }
+ S2CellId child = entry.id.child_begin();
+ // We already know that it has too many cells, so process its children.
+ // Each child may either be processed directly or enqueued again. The
+ // loop is optimized so that we don't seek unnecessarily.
+ bool seek = true;
+ NonEmptyRangeIterator range(index_);
+ for (int i = 0; i < 4; ++i, child = child.next()) {
+ seek = ProcessOrEnqueue(child, &range, seek);
+ }
+ }
+}
+
+template <class Distance>
+void S2ClosestCellQueryBase<Distance>::InitQueue() {
+ S2_DCHECK(queue_.empty());
+
+ // Optimization: rather than starting with the entire index, see if we can
+ // limit the search region to a small disc. Then we can find a covering for
+ // that disc and intersect it with the covering for the index. This can
+ // save a lot of work when the search region is small.
+ S2Cap cap = target_->GetCapBound();
+ if (cap.is_empty()) return; // Empty target.
+ if (options().max_results() == 1) {
+ // If the user is searching for just the closest cell, we can compute an
+ // upper bound on search radius by seeking to the center of the target's
+ // bounding cap and looking at the contents of that leaf cell range. If
+ // the range intersects any cells, then the distance is zero. Otherwise
+ // we can still look at the two neighboring ranges, and use the minimum
+ // distance to any cell in those ranges as an upper bound on the search
+ // radius. These cells may wind up being processed twice, but in general
+ // this is still faster.
+ //
+ // First check the range containing or immediately following "center".
+ NonEmptyRangeIterator range(index_);
+ S2CellId target(cap.center());
+ range.Seek(target);
+ AddRange(range);
+ if (distance_limit_ == Distance::Zero()) return;
+
+ // If the range immediately follows "center" (rather than containing it),
+ // then check the previous non-empty range as well.
+ if (range.start_id() > target && range.Prev()) {
+ AddRange(range);
+ if (distance_limit_ == Distance::Zero()) return;
+ }
+ }
+
+ // We start with a covering of the set of indexed cells, then intersect it
+ // with the maximum search radius disc (if any).
+ //
+ // Note that unlike S2ClosestPointQuery, we can't also intersect with the
+ // given region (if any). This is because the index cells in the result are
+ // only required to intersect the region. This means that an index cell that
+ // intersects the region's covering may be much closer to the target than the
+ // covering itself, which means that we cannot use the region's covering to
+ // restrict the search.
+ //
+ // TODO(ericv): If this feature becomes important, this could be fixed by
+ // (1) computing a covering of the region, (2) looking up any index cells
+ // that contain each covering cell by seeking to covering_cell.range_min(),
+ // (3) replacing each covering cell by the largest such cell (if any), and
+ // (4) normalizing the result.
+ if (index_covering_.empty()) InitCovering();
+ const std::vector<S2CellId>* initial_cells = &index_covering_;
+ if (distance_limit_ < Distance::Infinity()) {
+ S2RegionCoverer coverer;
+ coverer.mutable_options()->set_max_cells(4);
+ S1ChordAngle radius = cap.radius() + distance_limit_.GetChordAngleBound();
+ S2Cap search_cap(cap.center(), radius);
+ coverer.GetFastCovering(search_cap, &max_distance_covering_);
+ S2CellUnion::GetIntersection(*initial_cells, max_distance_covering_,
+ &intersection_with_max_distance_);
+ initial_cells = &intersection_with_max_distance_;
+ }
+ NonEmptyRangeIterator range(index_);
+ for (int i = 0; i < initial_cells->size(); ++i) {
+ S2CellId id = (*initial_cells)[i];
+ bool seek = (i == 0) || id.range_min() >= range.limit_id();
+ ProcessOrEnqueue(id, &range, seek);
+ if (range.done()) break;
+ }
+}
+
+template <class Distance>
+void S2ClosestCellQueryBase<Distance>::InitCovering() {
+ // Compute the "index covering", which is a small number of S2CellIds that
+ // cover the indexed cells. There are two cases:
+ //
+ // - If the index spans more than one face, then there is one covering cell
+ // per spanned face, just big enough to cover the indexed cells on that face.
+ //
+ // - If the index spans only one face, then we find the smallest cell "C"
+ // that covers the indexed cells on that face (just like the case above).
+ // Then for each of the 4 children of "C", if the child contains any index
+ // cells then we create a covering cell that is big enough to just fit
+ // those indexed cells (i.e., shrinking the child as much as possible to fit
+ // its contents). This essentially replicates what would happen if we
+ // started with "C" as the covering cell, since "C" would immediately be
+ // split, except that we take the time to prune the children further since
+ // this will save work on every subsequent query.
+ index_covering_.reserve(6);
+ NonEmptyRangeIterator it(index_), last(index_);
+ it.Begin();
+ last.Finish();
+ if (!last.Prev()) return; // Empty index.
+ S2CellId index_last_id = last.limit_id().prev();
+ if (it.start_id() != last.start_id()) {
+ // The index contains at least two distinct S2CellIds (because otherwise
+ // there would only be one non-empty range). Choose a level such that the
+ // entire index can be spanned with at most 6 cells (if the index spans
+ // multiple faces) or 4 cells (it the index spans a single face).
+ int level = it.start_id().GetCommonAncestorLevel(index_last_id) + 1;
+
+ // Visit each potential covering cell except the last (handled below).
+ S2CellId start_id = it.start_id().parent(level);
+ S2CellId last_id = index_last_id.parent(level);
+ for (S2CellId id = start_id; id != last_id; id = id.next()) {
+ // Skip any covering cells that don't contain an indexed range.
+ if (id.range_max() < it.start_id()) continue;
+
+ // Find the indexed range contained by this covering cell and then
+ // shrink the cell if necessary so that it just covers this range.
+ S2CellId cell_first_id = it.start_id();
+ it.Seek(id.range_max().next());
+ // Find the last leaf cell covered by the previous non-empty range.
+ last = it;
+ last.Prev();
+ AddInitialRange(cell_first_id, last.limit_id().prev());
+ }
+ }
+ AddInitialRange(it.start_id(), index_last_id);
+}
+
+// Adds a cell to index_covering_ that covers the given inclusive range.
+//
+// REQUIRES: "first" and "last" have a common ancestor.
+template <class Distance>
+void S2ClosestCellQueryBase<Distance>::AddInitialRange(
+ S2CellId first_id, S2CellId last_id) {
+ // Add the lowest common ancestor of the given range.
+ int level = first_id.GetCommonAncestorLevel(last_id);
+ S2_DCHECK_GE(level, 0);
+ index_covering_.push_back(first_id.parent(level));
+}
+
+// TODO(ericv): Consider having this method return false when distance_limit_
+// is reduced to zero, and terminating any calling loops early.
+template <class Distance>
+void S2ClosestCellQueryBase<Distance>::MaybeAddResult(S2CellId cell_id,
+ Label label) {
+ if (avoid_duplicates_ &&
+ !tested_cells_.insert(LabelledCell(cell_id, label)).second) {
+ return;
+ }
+
+ // TODO(ericv): It may be relatively common to add the same S2CellId
+ // multiple times with different labels. This could be optimized by
+ // remembering the last "cell_id" argument and its distance. However this
+ // may not be beneficial when Options::max_results() == 1, for example.
+ S2Cell cell(cell_id);
+ Distance distance = distance_limit_;
+ if (!target_->UpdateMinDistance(cell, &distance)) return;
+
+ const S2Region* region = options().region();
+ if (region && !region->MayIntersect(cell)) return;
+
+ Result result(distance, cell_id, label);
+ if (options().max_results() == 1) {
+ // Optimization for the common case where only the closest cell is wanted.
+ result_singleton_ = result;
+ distance_limit_ = result.distance() - options().max_error();
+ } else if (options().max_results() == Options::kMaxMaxResults) {
+ result_vector_.push_back(result); // Sort/unique at end.
+ } else {
+ // Add this cell to result_set_. Note that even if we already have enough
+ // edges, we can't erase an element before insertion because the "new"
+ // edge might in fact be a duplicate.
+ result_set_.insert(result);
+ int size = result_set_.size();
+ if (size >= options().max_results()) {
+ if (size > options().max_results()) {
+ result_set_.erase(--result_set_.end());
+ }
+ distance_limit_ = (--result_set_.end())->distance() -
+ options().max_error();
+ }
+ }
+}
+
+// Either process the contents of the given cell immediately, or add it to the
+// queue to be subdivided. If "seek" is false, then "iter" must be positioned
+// at the first non-empty range (if any) with start_id() >= id.range_min().
+//
+// Returns "true" if the cell was added to the queue, and "false" if it was
+// processed immediately, in which case "iter" is positioned at the first
+// non-empty range (if any) with start_id() > id.range_max().
+template <class Distance>
+bool S2ClosestCellQueryBase<Distance>::ProcessOrEnqueue(
+ S2CellId id, NonEmptyRangeIterator* iter, bool seek) {
+ if (seek) iter->Seek(id.range_min());
+ S2CellId last = id.range_max();
+ if (iter->start_id() > last) {
+ return false; // No need to seek to next child.
+ }
+ // If this cell intersects at least "kMinRangesToEnqueue" leaf cell ranges
+ // (including ranges whose contents are empty), then enqueue it. We test
+ // this by advancing (n - 1) ranges and checking whether that range also
+ // intersects this cell.
+ RangeIterator max_it = *iter;
+ if (max_it.Advance(kMinRangesToEnqueue - 1) && max_it.start_id() <= last) {
+ // This cell intersects at least kMinRangesToEnqueue ranges, so enqueue it.
+ S2Cell cell(id);
+ Distance distance = distance_limit_;
+ // We check "region_" second because it may be relatively expensive.
+ if (target_->UpdateMinDistance(cell, &distance) &&
+ (!options().region() || options().region()->MayIntersect(cell))) {
+ if (use_conservative_cell_distance_) {
+ // Ensure that "distance" is a lower bound on distance to the cell.
+ distance = distance - options().max_error();
+ }
+ queue_.push(QueueEntry(distance, id));
+ }
+ return true; // Seek to next child.
+ }
+ // There were few enough ranges that we might as well process them now.
+ for (; iter->start_id() <= last; iter->Next()) {
+ AddRange(*iter);
+ }
+ return false; // No need to seek to next child.
+}
+
+template <class Distance>
+void S2ClosestCellQueryBase<Distance>::AddRange(const RangeIterator& range) {
+ for (contents_it_.StartUnion(range);
+ !contents_it_.done(); contents_it_.Next()) {
+ MaybeAddResult(contents_it_.cell_id(), contents_it_.label());
+ }
+}
+
+#endif // S2_S2CLOSEST_CELL_QUERY_BASE_H_
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2CLOSEST_EDGE_QUERY_H_
+#define S2_S2CLOSEST_EDGE_QUERY_H_
+
+#include <memory>
+#include <queue>
+#include <type_traits>
+#include <vector>
+
+#include "s2/base/logging.h"
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/third_party/absl/container/inlined_vector.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/s1angle.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s2cell.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2closest_edge_query_base.h"
+#include "s2/s2edge_distances.h"
+#include "s2/s2min_distance_targets.h"
+#include "s2/s2shape_index.h"
+
+// S2ClosestEdgeQuery is a helper class for finding the closest edge(s) to a
+// given point, edge, S2Cell, or geometry collection. For example, given a
+// set of polylines, the following code efficiently finds the closest 5 edges
+// to a query point:
+//
+// void Test(const vector<S2Polyline*>& polylines, const S2Point& point) {
+// MutableS2ShapeIndex index;
+// for (S2Polyline* polyline : polylines) {
+// index.Add(new S2Polyline::Shape(polyline));
+// }
+// S2ClosestEdgeQuery query(&index);
+// query.mutable_options()->set_max_results(5);
+// S2ClosestEdgeQuery::PointTarget target(point);
+// for (const auto& result : query.FindClosestEdges(&target)) {
+// // The Result struct contains the following fields:
+// // "distance" is the distance to the edge.
+// // "shape_id" identifies the S2Shape containing the edge.
+// // "edge_id" identifies the edge with the given shape.
+// // The following convenience methods may also be useful:
+// // query.GetEdge(result) returns the endpoints of the edge.
+// // query.Project(point, result) computes the closest point on the
+// // result edge to the given target point.
+// int polyline_index = result.shape_id;
+// int edge_index = result.edge_id;
+// S1ChordAngle distance = result.distance; // Use ToAngle() for S1Angle.
+// S2Shape::Edge edge = query.GetEdge(result);
+// S2Point closest_point = query.Project(point, result);
+// }
+// }
+//
+// You can find either the k closest edges, or all edges within a given
+// radius, or both (i.e., the k closest edges up to a given maximum radius).
+// E.g. to find all the edges within 5 kilometers, call
+//
+// query.mutable_options()->set_max_distance(
+// S2Earth::ToAngle(util::units::Kilometers(5)));
+//
+// By default *all* edges are returned, so you should always specify either
+// max_results() or max_distance() or both. There is also a FindClosestEdge()
+// convenience method that returns only the closest edge.
+//
+// Note that by default, distances are measured to the boundary and interior
+// of polygons. For example, if a point is inside a polygon then its distance
+// is zero. To change this behavior, call set_include_interiors(false).
+//
+// If you only need to test whether the distance is above or below a given
+// threshold (e.g., 10 km), you can use the IsDistanceLess() method. This is
+// much faster than actually calculating the distance with FindClosestEdge(),
+// since the implementation can stop as soon as it can prove that the minimum
+// distance is either above or below the threshold.
+//
+// To find the closest edges to a query edge rather than a point, use:
+//
+// S2ClosestEdgeQuery::EdgeTarget target(v0, v1);
+// query.FindClosestEdges(&target);
+//
+// Similarly you can find the closest edges to an S2Cell by using an
+// S2ClosestEdgeQuery::CellTarget, and you can find the closest edges to an
+// arbitrary collection of points, polylines, and polygons by using an
+// S2ClosestEdgeQuery::ShapeIndexTarget.
+//
+// The implementation is designed to be fast for both simple and complex
+// geometric objects.
+class S2ClosestEdgeQuery {
+ public:
+ // See S2ClosestEdgeQueryBase for full documentation.
+
+ // S2MinDistance is a thin wrapper around S1ChordAngle that implements the
+ // Distance concept required by S2ClosestPointQueryBase.
+ using Distance = S2MinDistance;
+ using Base = S2ClosestEdgeQueryBase<Distance>;
+
+ // Each "Result" object represents a closest edge. It has the following
+ // fields:
+ //
+ // S1ChordAngle distance; // The distance from the target to this edge.
+ // int32 shape_id; // Identifies an indexed shape.
+ // int32 edge_id; // Identifies an edge within the shape.
+ using Result = Base::Result;
+
+ // Options that control the set of edges returned. Note that by default
+ // *all* edges are returned, so you will always want to set either the
+ // max_results() option or the max_distance() option (or both).
+ class Options : public Base::Options {
+ public:
+ // See S2ClosestEdgeQueryBase::Options for the full set of options.
+
+ // Specifies that only edges whose distance to the target is less than
+ // "max_distance" should be returned.
+ //
+ // Note that edges whose distance is exactly equal to "max_distance" are
+ // not returned. Normally this doesn't matter, because distances are not
+ // computed exactly in the first place, but if such edges are needed then
+ // see set_inclusive_max_distance() below.
+ //
+ // DEFAULT: Distance::Infinity()
+ void set_max_distance(S1ChordAngle max_distance);
+
+ // Like set_max_distance(), except that edges whose distance is exactly
+ // equal to "max_distance" are also returned. Equivalent to calling
+ // set_max_distance(max_distance.Successor()).
+ void set_inclusive_max_distance(S1ChordAngle max_distance);
+
+ // Like set_inclusive_max_distance(), except that "max_distance" is also
+ // increased by the maximum error in the distance calculation. This
+ // ensures that all edges whose true distance is less than or equal to
+ // "max_distance" will be returned (along with some edges whose true
+ // distance is slightly greater).
+ //
+ // Algorithms that need to do exact distance comparisons can use this
+ // option to find a set of candidate edges that can then be filtered
+ // further (e.g., using s2pred::CompareDistance).
+ void set_conservative_max_distance(S1ChordAngle max_distance);
+
+ // Versions of set_max_distance that take an S1Angle argument. (Note that
+ // these functions require a conversion, and that the S1ChordAngle versions
+ // are preferred.)
+ void set_max_distance(S1Angle max_distance);
+ void set_inclusive_max_distance(S1Angle max_distance);
+ void set_conservative_max_distance(S1Angle max_distance);
+
+ // See S2ClosestEdgeQueryBase::Options for documentation.
+ using Base::Options::set_max_error; // S1Chordangle version
+ void set_max_error(S1Angle max_error); // S1Angle version
+
+ // Inherited options (see s2closest_edge_query_base.h for details):
+ using Base::Options::set_max_results;
+ using Base::Options::set_include_interiors;
+ using Base::Options::set_use_brute_force;
+ };
+
+ // "Target" represents the geometry to which the distance is measured.
+ // There are subtypes for measuring the distance to a point, an edge, an
+ // S2Cell, or an S2ShapeIndex (an arbitrary collection of geometry).
+ using Target = S2MinDistanceTarget;
+
+ // Target subtype that computes the closest distance to a point.
+ class PointTarget final : public S2MinDistancePointTarget {
+ public:
+ explicit PointTarget(const S2Point& point);
+ int max_brute_force_index_size() const override;
+ };
+
+ // Target subtype that computes the closest distance to an edge.
+ class EdgeTarget final : public S2MinDistanceEdgeTarget {
+ public:
+ explicit EdgeTarget(const S2Point& a, const S2Point& b);
+ int max_brute_force_index_size() const override;
+ };
+
+ // Target subtype that computes the closest distance to an S2Cell
+ // (including the interior of the cell).
+ class CellTarget final : public S2MinDistanceCellTarget {
+ public:
+ explicit CellTarget(const S2Cell& cell);
+ int max_brute_force_index_size() const override;
+ };
+
+ // Target subtype that computes the closest distance to an S2ShapeIndex
+ // (an arbitrary collection of points, polylines, and/or polygons).
+ //
+ // By default, distances are measured to the boundary and interior of
+ // polygons in the S2ShapeIndex rather than to polygon boundaries only.
+ // If you wish to change this behavior, you may call
+ //
+ // target.set_include_interiors(false);
+ //
+ // (see S2MinDistanceShapeIndexTarget for details).
+ class ShapeIndexTarget final : public S2MinDistanceShapeIndexTarget {
+ public:
+ explicit ShapeIndexTarget(const S2ShapeIndex* index);
+ int max_brute_force_index_size() const override;
+ };
+
+ // Convenience constructor that calls Init(). Options may be specified here
+ // or changed at any time using the mutable_options() accessor method.
+ explicit S2ClosestEdgeQuery(const S2ShapeIndex* index,
+ const Options& options = Options());
+
+ // Default constructor; requires Init() to be called.
+ S2ClosestEdgeQuery();
+ ~S2ClosestEdgeQuery();
+
+ // Initializes the query. Options may be specified here or changed at any
+ // time using the mutable_options() accessor method.
+ //
+ // REQUIRES: "index" must persist for the lifetime of this object.
+ // REQUIRES: ReInit() must be called if "index" is modified.
+ void Init(const S2ShapeIndex* index, const Options& options = Options());
+
+ // Reinitializes the query. This method must be called whenever the
+ // underlying S2ShapeIndex is modified.
+ void ReInit();
+
+ // Returns a reference to the underlying S2ShapeIndex.
+ const S2ShapeIndex& index() const;
+
+ // Returns the query options. Options can be modified between queries.
+ const Options& options() const;
+ Options* mutable_options();
+
+ // Returns the closest edges to the given target that satisfy the current
+ // options. This method may be called multiple times.
+ //
+ // Note that if options().include_interiors() is true, the result vector may
+ // include some entries with edge_id == -1. This indicates that the target
+ // intersects the indexed polygon with the given shape_id.
+ std::vector<Result> FindClosestEdges(Target* target);
+
+ // This version can be more efficient when this method is called many times,
+ // since it does not require allocating a new vector on each call.
+ void FindClosestEdges(Target* target, std::vector<Result>* results);
+
+ //////////////////////// Convenience Methods ////////////////////////
+
+ // Returns the closest edge to the target. If no edge satisfies the search
+ // criteria, then the Result object will have distance == Infinity(),
+ // is_empty() == true, and shape_id == edge_id == -1.
+ //
+ // Note that if options.include_interiors() is true, edge_id == -1 is also
+ // used to indicate that the target intersects an indexed polygon (but in
+ // that case distance == Zero() and shape_id >= 0).
+ Result FindClosestEdge(Target* target);
+
+ // Returns the minimum distance to the target. If the index or target is
+ // empty, returns S1ChordAngle::Infinity().
+ //
+ // Use IsDistanceLess() if you only want to compare the distance against a
+ // threshold value, since it is often much faster.
+ S1ChordAngle GetDistance(Target* target);
+
+ // Returns true if the distance to "target" is less than "limit".
+ //
+ // This method is usually much faster than GetDistance(), since it is much
+ // less work to determine whether the minimum distance is above or below a
+ // threshold than it is to calculate the actual minimum distance.
+ bool IsDistanceLess(Target* target, S1ChordAngle limit);
+
+ // Like IsDistanceLess(), but also returns true if the distance to "target"
+ // is exactly equal to "limit".
+ bool IsDistanceLessOrEqual(Target* target, S1ChordAngle limit);
+
+ // Like IsDistanceLessOrEqual(), except that "limit" is increased by the
+ // maximum error in the distance calculation. This ensures that this
+ // function returns true whenever the true, exact distance is less than
+ // or equal to "limit".
+ //
+ // For example, suppose that we want to test whether two geometries might
+ // intersect each other after they are snapped together using S2Builder
+ // (using the IdentitySnapFunction with a given "snap_radius"). Since
+ // S2Builder uses exact distance predicates (s2predicates.h), we need to
+ // measure the distance between the two geometries conservatively. If the
+ // distance is definitely greater than "snap_radius", then the geometries
+ // are guaranteed to not intersect after snapping.
+ bool IsConservativeDistanceLessOrEqual(Target* target, S1ChordAngle limit);
+
+ // Returns the endpoints of the given result edge.
+ //
+ // CAVEAT: If options().include_interiors() is true, then clients must not
+ // pass this method any Result objects that correspond to shape interiors,
+ // i.e. those where result.edge_id < 0.
+ //
+ // REQUIRES: result.edge_id >= 0
+ S2Shape::Edge GetEdge(const Result& result) const;
+
+ // Returns the point on given result edge that is closest to "point".
+ S2Point Project(const S2Point& point, const Result& result) const;
+
+ private:
+ Options options_;
+ Base base_;
+
+ S2ClosestEdgeQuery(const S2ClosestEdgeQuery&) = delete;
+ void operator=(const S2ClosestEdgeQuery&) = delete;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline void S2ClosestEdgeQuery::Options::set_max_distance(
+ S1ChordAngle max_distance) {
+ Base::Options::set_max_distance(Distance(max_distance));
+}
+
+inline void S2ClosestEdgeQuery::Options::set_max_distance(
+ S1Angle max_distance) {
+ Base::Options::set_max_distance(Distance(max_distance));
+}
+
+inline void S2ClosestEdgeQuery::Options::set_inclusive_max_distance(
+ S1ChordAngle max_distance) {
+ set_max_distance(max_distance.Successor());
+}
+
+inline void S2ClosestEdgeQuery::Options::set_inclusive_max_distance(
+ S1Angle max_distance) {
+ set_inclusive_max_distance(S1ChordAngle(max_distance));
+}
+
+inline void S2ClosestEdgeQuery::Options::set_max_error(S1Angle max_error) {
+ Base::Options::set_max_error(S1ChordAngle(max_error));
+}
+
+inline S2ClosestEdgeQuery::PointTarget::PointTarget(const S2Point& point)
+ : S2MinDistancePointTarget(point) {
+}
+
+inline S2ClosestEdgeQuery::EdgeTarget::EdgeTarget(const S2Point& a,
+ const S2Point& b)
+ : S2MinDistanceEdgeTarget(a, b) {
+}
+
+inline S2ClosestEdgeQuery::CellTarget::CellTarget(const S2Cell& cell)
+ : S2MinDistanceCellTarget(cell) {
+}
+
+inline S2ClosestEdgeQuery::ShapeIndexTarget::ShapeIndexTarget(
+ const S2ShapeIndex* index)
+ : S2MinDistanceShapeIndexTarget(index) {
+}
+
+inline S2ClosestEdgeQuery::S2ClosestEdgeQuery(const S2ShapeIndex* index,
+ const Options& options) {
+ Init(index, options);
+}
+
+inline void S2ClosestEdgeQuery::Init(const S2ShapeIndex* index,
+ const Options& options) {
+ options_ = options;
+ base_.Init(index);
+}
+
+inline void S2ClosestEdgeQuery::ReInit() {
+ base_.ReInit();
+}
+
+inline const S2ShapeIndex& S2ClosestEdgeQuery::index() const {
+ return base_.index();
+}
+
+inline const S2ClosestEdgeQuery::Options& S2ClosestEdgeQuery::options() const {
+ return options_;
+}
+
+inline S2ClosestEdgeQuery::Options* S2ClosestEdgeQuery::mutable_options() {
+ return &options_;
+}
+
+inline std::vector<S2ClosestEdgeQuery::Result>
+S2ClosestEdgeQuery::FindClosestEdges(Target* target) {
+ return base_.FindClosestEdges(target, options_);
+}
+
+inline void S2ClosestEdgeQuery::FindClosestEdges(Target* target,
+ std::vector<Result>* results) {
+ base_.FindClosestEdges(target, options_, results);
+}
+
+inline S2ClosestEdgeQuery::Result S2ClosestEdgeQuery::FindClosestEdge(
+ Target* target) {
+ static_assert(sizeof(Options) <= 32, "Consider not copying Options here");
+ Options tmp_options = options_;
+ tmp_options.set_max_results(1);
+ return base_.FindClosestEdge(target, tmp_options);
+}
+
+inline S1ChordAngle S2ClosestEdgeQuery::GetDistance(Target* target) {
+ return FindClosestEdge(target).distance();
+}
+
+inline S2Shape::Edge S2ClosestEdgeQuery::GetEdge(const Result& result) const {
+ return index().shape(result.shape_id())->edge(result.edge_id());
+}
+
+inline S2Point S2ClosestEdgeQuery::Project(const S2Point& point,
+ const Result& result) const {
+ if (result.edge_id() < 0) return point;
+ auto edge = GetEdge(result);
+ return S2::Project(point, edge.v0, edge.v1);
+}
+
+#endif // S2_S2CLOSEST_EDGE_QUERY_H_
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2CLOSEST_EDGE_QUERY_BASE_H_
+#define S2_S2CLOSEST_EDGE_QUERY_BASE_H_
+
+#include <memory>
+#include <vector>
+
+#include "s2/base/logging.h"
+#include "s2/util/gtl/btree_set.h"
+#include "s2/third_party/absl/container/inlined_vector.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/s1angle.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s2cap.h"
+#include "s2/s2cell.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2cell_union.h"
+#include "s2/s2distance_target.h"
+#include "s2/s2region_coverer.h"
+#include "s2/s2shape_index.h"
+#include "s2/s2shapeutil_count_edges.h"
+#include "s2/s2shapeutil_shape_edge_id.h"
+#include "s2/util/gtl/dense_hash_set.h"
+
+// S2ClosestEdgeQueryBase is a templatized class for finding the closest
+// edge(s) between two geometries. It is not intended to be used directly,
+// but rather to serve as the implementation of various specialized classes
+// with more convenient APIs (such as S2ClosestEdgeQuery). It is flexible
+// enough so that it can be adapted to compute maximum distances and even
+// potentially Hausdorff distances.
+//
+// By using the appropriate options, this class can answer questions such as:
+//
+// - Find the minimum distance between two geometries A and B.
+// - Find all edges of geometry A that are within a distance D of geometry B.
+// - Find the k edges of geometry A that are closest to a given point P.
+//
+// You can also specify whether polygons should include their interiors (i.e.,
+// if a point is contained by a polygon, should the distance be zero or should
+// it be measured to the polygon boundary?)
+//
+// The input geometries may consist of any number of points, polylines, and
+// polygons (collectively referred to as "shapes"). Shapes do not need to be
+// disjoint; they may overlap or intersect arbitrarily. The implementation is
+// designed to be fast for both simple and complex geometries.
+//
+// The Distance template argument is used to represent distances. Usually it
+// is a thin wrapper around S1ChordAngle, but another distance type may be
+// used as long as it implements the Distance concept described in
+// s2distance_targets.h. For example this can be used to measure maximum
+// distances, to get more accuracy, or to measure non-spheroidal distances.
+template <class Distance>
+class S2ClosestEdgeQueryBase {
+ public:
+ using Delta = typename Distance::Delta;
+
+ // Options that control the set of edges returned. Note that by default
+ // *all* edges are returned, so you will always want to set either the
+ // max_results() option or the max_distance() option (or both).
+ class Options {
+ public:
+ Options();
+
+ // Specifies that at most "max_results" edges should be returned.
+ //
+ // REQUIRES: max_results >= 1
+ // DEFAULT: kMaxMaxResults
+ int max_results() const;
+ void set_max_results(int max_results);
+ static constexpr int kMaxMaxResults = std::numeric_limits<int>::max();
+
+ // Specifies that only edges whose distance to the target is less than
+ // "max_distance" should be returned.
+ //
+ // Note that edges whose distance is exactly equal to "max_distance" are
+ // not returned. In most cases this doesn't matter (since distances are
+ // not computed exactly in the first place), but if such edges are needed
+ // then you can retrieve them by specifying "max_distance" as the next
+ // largest representable Distance. For example, if Distance is an
+ // S1ChordAngle then you can specify max_distance.Successor().
+ //
+ // DEFAULT: Distance::Infinity()
+ Distance max_distance() const;
+ void set_max_distance(Distance max_distance);
+
+ // Specifies that edges up to max_error() further away than the true
+ // closest edges may be substituted in the result set, as long as such
+ // edges satisfy all the remaining search criteria (such as max_distance).
+ // This option only has an effect if max_results() is also specified;
+ // otherwise all edges closer than max_distance() will always be returned.
+ //
+ // Note that this does not affect how the distance between edges is
+ // computed; it simply gives the algorithm permission to stop the search
+ // early as soon as the best possible improvement drops below max_error().
+ //
+ // This can be used to implement distance predicates efficiently. For
+ // example, to determine whether the minimum distance is less than D, set
+ // max_results() == 1 and max_distance() == max_error() == D. This causes
+ // the algorithm to terminate as soon as it finds any edge whose distance
+ // is less than D, rather than continuing to search for an edge that is
+ // even closer.
+ //
+ // DEFAULT: Distance::Delta::Zero()
+ Delta max_error() const;
+ void set_max_error(Delta max_error);
+
+ // Specifies that polygon interiors should be included when measuring
+ // distances. In other words, polygons that contain the target should
+ // have a distance of zero. (For targets consisting of multiple connected
+ // components, the distance is zero if any component is contained.) This
+ // is indicated in the results by returning a (shape_id, edge_id) pair
+ // with edge_id == -1, i.e. this value denotes the polygons's interior.
+ //
+ // Note that for efficiency, any polygon that intersects the target may or
+ // may not have an (edge_id == -1) result. Such results are optional
+ // because in that case the distance to the polygon is already zero.
+ //
+ // DEFAULT: true
+ bool include_interiors() const;
+ void set_include_interiors(bool include_interiors);
+
+ // Specifies that distances should be computed by examining every edge
+ // rather than using the S2ShapeIndex. This is useful for testing,
+ // benchmarking, and debugging.
+ //
+ // DEFAULT: false
+ bool use_brute_force() const;
+ void set_use_brute_force(bool use_brute_force);
+
+ private:
+ Distance max_distance_ = Distance::Infinity();
+ Delta max_error_ = Delta::Zero();
+ int max_results_ = kMaxMaxResults;
+ bool include_interiors_ = true;
+ bool use_brute_force_ = false;
+ };
+
+ // The Target class represents the geometry to which the distance is
+ // measured. For example, there can be subtypes for measuring the distance
+ // to a point, an edge, or to an S2ShapeIndex (an arbitrary collection of
+ // geometry).
+ //
+ // Implementations do *not* need to be thread-safe. They may cache data or
+ // allocate temporary data structures in order to improve performance.
+ using Target = S2DistanceTarget<Distance>;
+
+ // Each "Result" object represents a closest edge. Note the following
+ // special cases:
+ //
+ // - (shape_id() >= 0) && (edge_id() < 0) represents the interior of a shape.
+ // Such results may be returned when options.include_interiors() is true.
+ // Such results can be identified using the is_interior() method.
+ //
+ // - (shape_id() < 0) && (edge_id() < 0) is returned by `FindClosestEdge`
+ // to indicate that no edge satisfies the given query options. Such
+ // results can be identified using is_empty() method.
+ class Result {
+ public:
+ // The default constructor yields an empty result, with a distance() of
+ // Infinity() and shape_id == edge_id == -1.
+ Result() : distance_(Distance::Infinity()), shape_id_(-1), edge_id_(-1) {}
+
+ // Constructs a Result object for the given arguments.
+ Result(Distance distance, int32 shape_id, int32 edge_id)
+ : distance_(distance), shape_id_(shape_id), edge_id_(edge_id) {}
+
+ // The distance from the target to this edge.
+ Distance distance() const { return distance_; }
+
+ // Identifies an indexed shape.
+ int32 shape_id() const { return shape_id_; }
+
+ // Identifies an edge within the shape.
+ int32 edge_id() const { return edge_id_; }
+
+ // Returns true if this Result object represents the interior of a shape.
+ // (Such results may be returned when options.include_interiors() is true.)
+ bool is_interior() const { return shape_id_ >= 0 && edge_id_ < 0; }
+
+ // Returns true if this Result object indicates that no edge satisfies the
+ // given query options. (This result is only returned in one special
+ // case, namely when FindClosestEdge() does not find any suitable edges.
+ // It is never returned by methods that return a vector of results.)
+ bool is_empty() const { return shape_id_ < 0; }
+
+ // Returns true if two Result objects are identical.
+ friend bool operator==(const Result& x, const Result& y) {
+ return (x.distance_ == y.distance_ &&
+ x.shape_id_ == y.shape_id_ &&
+ x.edge_id_ == y.edge_id_);
+ }
+
+ // Compares edges first by distance, then by (shape_id, edge_id).
+ friend bool operator<(const Result& x, const Result& y) {
+ if (x.distance_ < y.distance_) return true;
+ if (y.distance_ < x.distance_) return false;
+ if (x.shape_id_ < y.shape_id_) return true;
+ if (y.shape_id_ < x.shape_id_) return false;
+ return x.edge_id_ < y.edge_id_;
+ }
+
+ // Indicates that linear rather than binary search should be used when this
+ // type is used as the key in gtl::btree data structures.
+ using goog_btree_prefer_linear_node_search = std::true_type;
+
+ private:
+ Distance distance_; // The distance from the target to this edge.
+ int32 shape_id_; // Identifies an indexed shape.
+ int32 edge_id_; // Identifies an edge within the shape.
+ };
+
+ // Default constructor; requires Init() to be called.
+ S2ClosestEdgeQueryBase();
+ ~S2ClosestEdgeQueryBase();
+
+ // Convenience constructor that calls Init().
+ explicit S2ClosestEdgeQueryBase(const S2ShapeIndex* index);
+
+ // S2ClosestEdgeQueryBase is not copyable.
+ S2ClosestEdgeQueryBase(const S2ClosestEdgeQueryBase&) = delete;
+ void operator=(const S2ClosestEdgeQueryBase&) = delete;
+
+ // Initializes the query.
+ // REQUIRES: ReInit() must be called if "index" is modified.
+ void Init(const S2ShapeIndex* index);
+
+ // Reinitializes the query. This method must be called whenever the
+ // underlying index is modified.
+ void ReInit();
+
+ // Returns a reference to the underlying S2ShapeIndex.
+ const S2ShapeIndex& index() const;
+
+ // Returns the closest edges to the given target that satisfy the given
+ // options. This method may be called multiple times.
+ //
+ // Note that if options().include_interiors() is true, the result vector may
+ // include some entries with edge_id == -1. This indicates that the target
+ // intersects the indexed polygon with the given shape_id.
+ std::vector<Result> FindClosestEdges(Target* target, const Options& options);
+
+ // This version can be more efficient when this method is called many times,
+ // since it does not require allocating a new vector on each call.
+ void FindClosestEdges(Target* target, const Options& options,
+ std::vector<Result>* results);
+
+ // Convenience method that returns exactly one edge. If no edges satisfy
+ // the given search criteria, then a Result with distance == Infinity() and
+ // shape_id == edge_id == -1 is returned.
+ //
+ // Note that if options.include_interiors() is true, edge_id == -1 is also
+ // used to indicate that the target intersects an indexed polygon (but in
+ // that case distance == Zero() and shape_id >= 0).
+ //
+ // REQUIRES: options.max_results() == 1
+ Result FindClosestEdge(Target* target, const Options& options);
+
+ private:
+ struct QueueEntry;
+
+ const Options& options() const { return *options_; }
+ void FindClosestEdgesInternal(Target* target, const Options& options);
+ void FindClosestEdgesBruteForce();
+ void FindClosestEdgesOptimized();
+ void InitQueue();
+ void InitCovering();
+ void AddInitialRange(const S2ShapeIndex::Iterator& first,
+ const S2ShapeIndex::Iterator& last);
+ void MaybeAddResult(const S2Shape& shape, int edge_id);
+ void AddResult(const Result& result);
+ void ProcessEdges(const QueueEntry& entry);
+ void ProcessOrEnqueue(S2CellId id);
+ void ProcessOrEnqueue(S2CellId id, const S2ShapeIndexCell* index_cell);
+
+ const S2ShapeIndex* index_;
+ const Options* options_;
+ Target* target_;
+
+ // True if max_error() must be subtracted from priority queue cell distances
+ // in order to ensure that such distances are measured conservatively. This
+ // is true only if the target takes advantage of max_error() in order to
+ // return faster results, and 0 < max_error() < distance_limit_.
+ bool use_conservative_cell_distance_;
+
+ // For the optimized algorihm we precompute the top-level S2CellIds that
+ // will be added to the priority queue. There can be at most 6 of these
+ // cells. Essentially this is just a covering of the indexed edges, except
+ // that we also store pointers to the corresponding S2ShapeIndexCells to
+ // reduce the number of index seeks required.
+ //
+ // The covering needs to be stored in a std::vector so that we can use
+ // S2CellUnion::GetIntersection().
+ std::vector<S2CellId> index_covering_;
+ absl::InlinedVector<const S2ShapeIndexCell*, 6> index_cells_;
+
+ // The decision about whether to use the brute force algorithm is based on
+ // counting the total number of edges in the index. However if the index
+ // contains a large number of shapes, this in itself might take too long.
+ // So instead we only count edges up to (max_brute_force_index_size() + 1)
+ // for the current target type (stored as index_num_edges_limit_).
+ int index_num_edges_;
+ int index_num_edges_limit_;
+
+ // The distance beyond which we can safely ignore further candidate edges.
+ // (Candidates that are exactly at the limit are ignored; this is more
+ // efficient for UpdateMinDistance() and should not affect clients since
+ // distance measurements have a small amount of error anyway.)
+ //
+ // Initially this is the same as the maximum distance specified by the user,
+ // but it can also be updated by the algorithm (see MaybeAddResult).
+ Distance distance_limit_;
+
+ // The current result set is stored in one of three ways:
+ //
+ // - If max_results() == 1, the best result is kept in result_singleton_.
+ //
+ // - If max_results() == "infinity", results are appended to result_vector_
+ // and sorted/uniqued at the end.
+ //
+ // - Otherwise results are kept in a btree_set so that we can progressively
+ // reduce the distance limit once max_results() results have been found.
+ // (A priority queue is not sufficient because we need to be able to
+ // check whether a candidate edge is already in the result set.)
+ //
+ // TODO(ericv): Check whether it would be faster to use avoid_duplicates_
+ // when result_set_ is used so that we could use a priority queue instead.
+ Result result_singleton_;
+ std::vector<Result> result_vector_;
+ gtl::btree_set<Result> result_set_;
+
+ // When the result edges are stored in a btree_set (see above), usually
+ // duplicates can be removed simply by inserting candidate edges in the
+ // current set. However this is not true if Options::max_error() > 0 and
+ // the Target subtype takes advantage of this by returning suboptimal
+ // distances. This is because when UpdateMinDistance() is called with
+ // different "min_dist" parameters (i.e., the distance to beat), the
+ // implementation may return a different distance for the same edge. Since
+ // the btree_set is keyed by (distance, shape_id, edge_id) this can create
+ // duplicate edges in the results.
+ //
+ // The flag below is true when duplicates must be avoided explicitly. This
+ // is achieved by maintaining a separate set keyed by (shape_id, edge_id)
+ // only, and checking whether each edge is in that set before computing the
+ // distance to it.
+ //
+ // TODO(ericv): Check whether it is faster to avoid duplicates by default
+ // (even when Options::max_results() == 1), rather than just when we need to.
+ bool avoid_duplicates_;
+ using ShapeEdgeId = s2shapeutil::ShapeEdgeId;
+ gtl::dense_hash_set<ShapeEdgeId, s2shapeutil::ShapeEdgeIdHash> tested_edges_;
+
+ // The algorithm maintains a priority queue of unprocessed S2CellIds, sorted
+ // in increasing order of distance from the target.
+ struct QueueEntry {
+ // A lower bound on the distance from the target to "id". This is the key
+ // of the priority queue.
+ Distance distance;
+
+ // The cell being queued.
+ S2CellId id;
+
+ // If "id" belongs to the index, this field stores the corresponding
+ // S2ShapeIndexCell. Otherwise "id" is a proper ancestor of one or more
+ // S2ShapeIndexCells and this field stores nullptr. The purpose of this
+ // field is to avoid an extra Seek() when the queue entry is processed.
+ const S2ShapeIndexCell* index_cell;
+
+ QueueEntry(Distance _distance, S2CellId _id,
+ const S2ShapeIndexCell* _index_cell)
+ : distance(_distance), id(_id), index_cell(_index_cell) {
+ }
+ bool operator<(const QueueEntry& other) const {
+ // The priority queue returns the largest elements first, so we want the
+ // "largest" entry to have the smallest distance.
+ return other.distance < distance;
+ }
+ };
+ using CellQueue =
+ std::priority_queue<QueueEntry, absl::InlinedVector<QueueEntry, 16>>;
+ CellQueue queue_;
+
+ // Temporaries, defined here to avoid multiple allocations / initializations.
+
+ S2ShapeIndex::Iterator iter_;
+ std::vector<S2CellId> max_distance_covering_;
+ std::vector<S2CellId> initial_cells_;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+template <class Distance>
+inline S2ClosestEdgeQueryBase<Distance>::Options::Options() {
+}
+
+template <class Distance>
+inline int S2ClosestEdgeQueryBase<Distance>::Options::max_results() const {
+ return max_results_;
+}
+
+template <class Distance>
+inline void S2ClosestEdgeQueryBase<Distance>::Options::set_max_results(
+ int max_results) {
+ S2_DCHECK_GE(max_results, 1);
+ max_results_ = max_results;
+}
+
+template <class Distance>
+inline Distance S2ClosestEdgeQueryBase<Distance>::Options::max_distance()
+ const {
+ return max_distance_;
+}
+
+template <class Distance>
+inline void S2ClosestEdgeQueryBase<Distance>::Options::set_max_distance(
+ Distance max_distance) {
+ max_distance_ = max_distance;
+}
+
+template <class Distance>
+inline typename Distance::Delta
+S2ClosestEdgeQueryBase<Distance>::Options::max_error() const {
+ return max_error_;
+}
+
+template <class Distance>
+inline void S2ClosestEdgeQueryBase<Distance>::Options::set_max_error(
+ Delta max_error) {
+ max_error_ = max_error;
+}
+
+template <class Distance>
+inline bool S2ClosestEdgeQueryBase<Distance>::Options::include_interiors()
+ const {
+ return include_interiors_;
+}
+
+template <class Distance>
+inline void S2ClosestEdgeQueryBase<Distance>::Options::set_include_interiors(
+ bool include_interiors) {
+ include_interiors_ = include_interiors;
+}
+
+template <class Distance>
+inline bool S2ClosestEdgeQueryBase<Distance>::Options::use_brute_force() const {
+ return use_brute_force_;
+}
+
+template <class Distance>
+inline void S2ClosestEdgeQueryBase<Distance>::Options::set_use_brute_force(
+ bool use_brute_force) {
+ use_brute_force_ = use_brute_force;
+}
+
+template <class Distance>
+S2ClosestEdgeQueryBase<Distance>::S2ClosestEdgeQueryBase()
+ : tested_edges_(1) /* expected_max_elements*/ {
+ tested_edges_.set_empty_key(ShapeEdgeId(-1, -1));
+}
+
+template <class Distance>
+S2ClosestEdgeQueryBase<Distance>::~S2ClosestEdgeQueryBase() {
+ // Prevent inline destructor bloat by providing a definition.
+}
+
+template <class Distance>
+inline S2ClosestEdgeQueryBase<Distance>::S2ClosestEdgeQueryBase(
+ const S2ShapeIndex* index) : S2ClosestEdgeQueryBase() {
+ Init(index);
+}
+
+template <class Distance>
+void S2ClosestEdgeQueryBase<Distance>::Init(const S2ShapeIndex* index) {
+ index_ = index;
+ ReInit();
+}
+
+template <class Distance>
+void S2ClosestEdgeQueryBase<Distance>::ReInit() {
+ index_num_edges_ = 0;
+ index_num_edges_limit_ = 0;
+ index_covering_.clear();
+ index_cells_.clear();
+ // We don't initialize iter_ here to make queries on small indexes a bit
+ // faster (i.e., where brute force is used).
+}
+
+template <class Distance>
+inline const S2ShapeIndex& S2ClosestEdgeQueryBase<Distance>::index() const {
+ return *index_;
+}
+
+template <class Distance>
+inline std::vector<typename S2ClosestEdgeQueryBase<Distance>::Result>
+S2ClosestEdgeQueryBase<Distance>::FindClosestEdges(Target* target,
+ const Options& options) {
+ std::vector<Result> results;
+ FindClosestEdges(target, options, &results);
+ return results;
+}
+
+template <class Distance>
+typename S2ClosestEdgeQueryBase<Distance>::Result
+S2ClosestEdgeQueryBase<Distance>::FindClosestEdge(Target* target,
+ const Options& options) {
+ S2_DCHECK_EQ(options.max_results(), 1);
+ FindClosestEdgesInternal(target, options);
+ return result_singleton_;
+}
+
+template <class Distance>
+void S2ClosestEdgeQueryBase<Distance>::FindClosestEdges(
+ Target* target, const Options& options,
+ std::vector<Result>* results) {
+ FindClosestEdgesInternal(target, options);
+ results->clear();
+ if (options.max_results() == 1) {
+ if (result_singleton_.shape_id() >= 0) {
+ results->push_back(result_singleton_);
+ }
+ } else if (options.max_results() == Options::kMaxMaxResults) {
+ std::sort(result_vector_.begin(), result_vector_.end());
+ std::unique_copy(result_vector_.begin(), result_vector_.end(),
+ std::back_inserter(*results));
+ result_vector_.clear();
+ } else {
+ results->assign(result_set_.begin(), result_set_.end());
+ result_set_.clear();
+ }
+}
+
+template <class Distance>
+void S2ClosestEdgeQueryBase<Distance>::FindClosestEdgesInternal(
+ Target* target, const Options& options) {
+ target_ = target;
+ options_ = &options;
+
+ tested_edges_.clear();
+ distance_limit_ = options.max_distance();
+ result_singleton_ = Result();
+ S2_DCHECK(result_vector_.empty());
+ S2_DCHECK(result_set_.empty());
+ S2_DCHECK_GE(target->max_brute_force_index_size(), 0);
+ if (distance_limit_ == Distance::Zero()) return;
+
+ if (options.max_results() == Options::kMaxMaxResults &&
+ options.max_distance() == Distance::Infinity()) {
+ S2_LOG(WARNING) << "Returning all edges (max_results/max_distance not set)";
+ }
+
+ if (options.include_interiors()) {
+ gtl::btree_set<int32> shape_ids;
+ (void) target->VisitContainingShapes(
+ *index_, [&shape_ids, &options](S2Shape* containing_shape,
+ const S2Point& target_point) {
+ shape_ids.insert(containing_shape->id());
+ return shape_ids.size() < options.max_results();
+ });
+ for (int shape_id : shape_ids) {
+ AddResult(Result(Distance::Zero(), shape_id, -1));
+ }
+ if (distance_limit_ == Distance::Zero()) return;
+ }
+
+ // If max_error() > 0 and the target takes advantage of this, then we may
+ // need to adjust the distance estimates to the priority queue cells to
+ // ensure that they are always a lower bound on the true distance. For
+ // example, suppose max_distance == 100, max_error == 30, and we compute the
+ // distance to the target from some cell C0 as d(C0) == 80. Then because
+ // the target takes advantage of max_error(), the true distance could be as
+ // low as 50. In order not to miss edges contained by such cells, we need
+ // to subtract max_error() from the distance estimates. This behavior is
+ // controlled by the use_conservative_cell_distance_ flag.
+ //
+ // However there is one important case where this adjustment is not
+ // necessary, namely when max_distance() < max_error(). This is because
+ // max_error() only affects the algorithm once at least max_results() edges
+ // have been found that satisfy the given distance limit. At that point,
+ // max_error() is subtracted from distance_limit_ in order to ensure that
+ // any further matches are closer by at least that amount. But when
+ // max_distance() < max_error(), this reduces the distance limit to 0,
+ // i.e. all remaining candidate cells and edges can safely be discarded.
+ // (Note that this is how IsDistanceLess() and friends are implemented.)
+ //
+ // Note that Distance::Delta only supports operator==.
+ bool target_uses_max_error = (!(options.max_error() == Delta::Zero()) &&
+ target_->set_max_error(options.max_error()));
+
+ // Note that we can't compare max_error() and distance_limit_ directly
+ // because one is a Delta and one is a Distance. Instead we subtract them.
+ use_conservative_cell_distance_ = target_uses_max_error &&
+ (distance_limit_ == Distance::Infinity() ||
+ Distance::Zero() < distance_limit_ - options.max_error());
+
+ // Use the brute force algorithm if the index is small enough. To avoid
+ // spending too much time counting edges when there are many shapes, we stop
+ // counting once there are too many edges. We may need to recount the edges
+ // if we later see a target with a larger brute force edge threshold.
+ int min_optimized_edges = target_->max_brute_force_index_size() + 1;
+ if (min_optimized_edges > index_num_edges_limit_ &&
+ index_num_edges_ >= index_num_edges_limit_) {
+ index_num_edges_ = s2shapeutil::CountEdgesUpTo(*index_,
+ min_optimized_edges);
+ index_num_edges_limit_ = min_optimized_edges;
+ }
+
+ if (options.use_brute_force() || index_num_edges_ < min_optimized_edges) {
+ // The brute force algorithm considers each edge exactly once.
+ avoid_duplicates_ = false;
+ FindClosestEdgesBruteForce();
+ } else {
+ // If the target takes advantage of max_error() then we need to avoid
+ // duplicate edges explicitly. (Otherwise it happens automatically.)
+ avoid_duplicates_ = (target_uses_max_error && options.max_results() > 1);
+ FindClosestEdgesOptimized();
+ }
+}
+
+template <class Distance>
+void S2ClosestEdgeQueryBase<Distance>::FindClosestEdgesBruteForce() {
+ for (S2Shape* shape : *index_) {
+ if (shape == nullptr) continue;
+ int num_edges = shape->num_edges();
+ for (int e = 0; e < num_edges; ++e) {
+ MaybeAddResult(*shape, e);
+ }
+ }
+}
+
+template <class Distance>
+void S2ClosestEdgeQueryBase<Distance>::FindClosestEdgesOptimized() {
+ InitQueue();
+ // Repeatedly find the closest S2Cell to "target" and either split it into
+ // its four children or process all of its edges.
+ while (!queue_.empty()) {
+ // We need to copy the top entry before removing it, and we need to
+ // remove it before adding any new entries to the queue.
+ QueueEntry entry = queue_.top();
+ queue_.pop();
+ // Work around weird parse error in gcc 4.9 by using a local variable for
+ // entry.distance.
+ Distance distance = entry.distance;
+ if (!(distance < distance_limit_)) {
+ queue_ = CellQueue(); // Clear any remaining entries.
+ break;
+ }
+ // If this is already known to be an index cell, just process it.
+ if (entry.index_cell != nullptr) {
+ ProcessEdges(entry);
+ continue;
+ }
+ // Otherwise split the cell into its four children. Before adding a
+ // child back to the queue, we first check whether it is empty. We do
+ // this in two seek operations rather than four by seeking to the key
+ // between children 0 and 1 and to the key between children 2 and 3.
+ S2CellId id = entry.id;
+ iter_.Seek(id.child(1).range_min());
+ if (!iter_.done() && iter_.id() <= id.child(1).range_max()) {
+ ProcessOrEnqueue(id.child(1));
+ }
+ if (iter_.Prev() && iter_.id() >= id.range_min()) {
+ ProcessOrEnqueue(id.child(0));
+ }
+ iter_.Seek(id.child(3).range_min());
+ if (!iter_.done() && iter_.id() <= id.range_max()) {
+ ProcessOrEnqueue(id.child(3));
+ }
+ if (iter_.Prev() && iter_.id() >= id.child(2).range_min()) {
+ ProcessOrEnqueue(id.child(2));
+ }
+ }
+}
+
+template <class Distance>
+void S2ClosestEdgeQueryBase<Distance>::InitQueue() {
+ S2_DCHECK(queue_.empty());
+ if (index_covering_.empty()) {
+ // We delay iterator initialization until now to make queries on very
+ // small indexes a bit faster (i.e., where brute force is used).
+ iter_.Init(index_, S2ShapeIndex::UNPOSITIONED);
+ }
+
+ // Optimization: if the user is searching for just the closest edge, and the
+ // center of the target's bounding cap happens to intersect an index cell,
+ // then we try to limit the search region to a small disc by first
+ // processing the edges in that cell. This sets distance_limit_ based on
+ // the closest edge in that cell, which we can then use to limit the search
+ // area. This means that the cell containing "target" will be processed
+ // twice, but in general this is still faster.
+ //
+ // TODO(ericv): Even if the cap center is not contained, we could still
+ // process one or both of the adjacent index cells in S2CellId order,
+ // provided that those cells are closer than distance_limit_.
+ S2Cap cap = target_->GetCapBound();
+ if (cap.is_empty()) return; // Empty target.
+ if (options().max_results() == 1 && iter_.Locate(cap.center())) {
+ ProcessEdges(QueueEntry(Distance::Zero(), iter_.id(), &iter_.cell()));
+ // Skip the rest of the algorithm if we found an intersecting edge.
+ if (distance_limit_ == Distance::Zero()) return;
+ }
+ if (index_covering_.empty()) InitCovering();
+ if (distance_limit_ == Distance::Infinity()) {
+ // Start with the precomputed index covering.
+ for (int i = 0; i < index_covering_.size(); ++i) {
+ ProcessOrEnqueue(index_covering_[i], index_cells_[i]);
+ }
+ } else {
+ // Compute a covering of the search disc and intersect it with the
+ // precomputed index covering.
+ S2RegionCoverer coverer;
+ coverer.mutable_options()->set_max_cells(4);
+ S1ChordAngle radius = cap.radius() + distance_limit_.GetChordAngleBound();
+ S2Cap search_cap(cap.center(), radius);
+ coverer.GetFastCovering(search_cap, &max_distance_covering_);
+ S2CellUnion::GetIntersection(index_covering_, max_distance_covering_,
+ &initial_cells_);
+
+ // Now we need to clean up the initial cells to ensure that they all
+ // contain at least one cell of the S2ShapeIndex. (Some may not intersect
+ // the index at all, while other may be descendants of an index cell.)
+ for (int i = 0, j = 0; i < initial_cells_.size(); ) {
+ S2CellId id_i = initial_cells_[i];
+ // Find the top-level cell that contains this initial cell.
+ while (index_covering_[j].range_max() < id_i) ++j;
+ S2CellId id_j = index_covering_[j];
+ if (id_i == id_j) {
+ // This initial cell is one of the top-level cells. Use the
+ // precomputed S2ShapeIndexCell pointer to avoid an index seek.
+ ProcessOrEnqueue(id_j, index_cells_[j]);
+ ++i, ++j;
+ } else {
+ // This initial cell is a proper descendant of a top-level cell.
+ // Check how it is related to the cells of the S2ShapeIndex.
+ S2ShapeIndex::CellRelation r = iter_.Locate(id_i);
+ if (r == S2ShapeIndex::INDEXED) {
+ // This cell is a descendant of an index cell. Enqueue it and skip
+ // any other initial cells that are also descendants of this cell.
+ ProcessOrEnqueue(iter_.id(), &iter_.cell());
+ const S2CellId last_id = iter_.id().range_max();
+ while (++i < initial_cells_.size() && initial_cells_[i] <= last_id)
+ continue;
+ } else {
+ // Enqueue the cell only if it contains at least one index cell.
+ if (r == S2ShapeIndex::SUBDIVIDED) ProcessOrEnqueue(id_i, nullptr);
+ ++i;
+ }
+ }
+ }
+ }
+}
+
+template <class Distance>
+void S2ClosestEdgeQueryBase<Distance>::InitCovering() {
+ // Find the range of S2Cells spanned by the index and choose a level such
+ // that the entire index can be covered with just a few cells. These are
+ // the "top-level" cells. There are two cases:
+ //
+ // - If the index spans more than one face, then there is one top-level cell
+ // per spanned face, just big enough to cover the index cells on that face.
+ //
+ // - If the index spans only one face, then we find the smallest cell "C"
+ // that covers the index cells on that face (just like the case above).
+ // Then for each of the 4 children of "C", if the child contains any index
+ // cells then we create a top-level cell that is big enough to just fit
+ // those index cells (i.e., shrinking the child as much as possible to fit
+ // its contents). This essentially replicates what would happen if we
+ // started with "C" as the top-level cell, since "C" would immediately be
+ // split, except that we take the time to prune the children further since
+ // this will save work on every subsequent query.
+
+ // Don't need to reserve index_cells_ since it is an InlinedVector.
+ index_covering_.reserve(6);
+
+ // TODO(ericv): Use a single iterator (iter_) below and save position
+ // information using pair<S2CellId, const S2ShapeIndexCell*> type.
+ S2ShapeIndex::Iterator next(index_, S2ShapeIndex::BEGIN);
+ S2ShapeIndex::Iterator last(index_, S2ShapeIndex::END);
+ last.Prev();
+ if (next.id() != last.id()) {
+ // The index has at least two cells. Choose a level such that the entire
+ // index can be spanned with at most 6 cells (if the index spans multiple
+ // faces) or 4 cells (it the index spans a single face).
+ int level = next.id().GetCommonAncestorLevel(last.id()) + 1;
+
+ // Visit each potential top-level cell except the last (handled below).
+ S2CellId last_id = last.id().parent(level);
+ for (S2CellId id = next.id().parent(level); id != last_id; id = id.next()) {
+ // Skip any top-level cells that don't contain any index cells.
+ if (id.range_max() < next.id()) continue;
+
+ // Find the range of index cells contained by this top-level cell and
+ // then shrink the cell if necessary so that it just covers them.
+ S2ShapeIndex::Iterator cell_first = next;
+ next.Seek(id.range_max().next());
+ S2ShapeIndex::Iterator cell_last = next;
+ cell_last.Prev();
+ AddInitialRange(cell_first, cell_last);
+ }
+ }
+ AddInitialRange(next, last);
+}
+
+// Add an entry to index_covering_ and index_cells_ that covers the given
+// inclusive range of cells.
+//
+// REQUIRES: "first" and "last" have a common ancestor.
+template <class Distance>
+void S2ClosestEdgeQueryBase<Distance>::AddInitialRange(
+ const S2ShapeIndex::Iterator& first,
+ const S2ShapeIndex::Iterator& last) {
+ if (first.id() == last.id()) {
+ // The range consists of a single index cell.
+ index_covering_.push_back(first.id());
+ index_cells_.push_back(&first.cell());
+ } else {
+ // Add the lowest common ancestor of the given range.
+ int level = first.id().GetCommonAncestorLevel(last.id());
+ S2_DCHECK_GE(level, 0);
+ index_covering_.push_back(first.id().parent(level));
+ index_cells_.push_back(nullptr);
+ }
+}
+
+template <class Distance>
+void S2ClosestEdgeQueryBase<Distance>::MaybeAddResult(
+ const S2Shape& shape, int edge_id) {
+ if (avoid_duplicates_ &&
+ !tested_edges_.insert(ShapeEdgeId(shape.id(), edge_id)).second) {
+ return;
+ }
+ auto edge = shape.edge(edge_id);
+ Distance distance = distance_limit_;
+ if (target_->UpdateMinDistance(edge.v0, edge.v1, &distance)) {
+ AddResult(Result(distance, shape.id(), edge_id));
+ }
+}
+
+template <class Distance>
+void S2ClosestEdgeQueryBase<Distance>::AddResult(const Result& result) {
+ if (options().max_results() == 1) {
+ // Optimization for the common case where only the closest edge is wanted.
+ result_singleton_ = result;
+ distance_limit_ = result.distance() - options().max_error();
+ } else if (options().max_results() == Options::kMaxMaxResults) {
+ result_vector_.push_back(result); // Sort/unique at end.
+ } else {
+ // Add this edge to result_set_. Note that even if we already have enough
+ // edges, we can't erase an element before insertion because the "new"
+ // edge might in fact be a duplicate.
+ result_set_.insert(result);
+ int size = result_set_.size();
+ if (size >= options().max_results()) {
+ if (size > options().max_results()) {
+ result_set_.erase(--result_set_.end());
+ }
+ distance_limit_ = (--result_set_.end())->distance() -
+ options().max_error();
+ }
+ }
+}
+
+// Return the number of edges in the given index cell.
+inline static int CountEdges(const S2ShapeIndexCell* cell) {
+ int count = 0;
+ for (int s = 0; s < cell->num_clipped(); ++s) {
+ count += cell->clipped(s).num_edges();
+ }
+ return count;
+}
+
+// Process all the edges of the given index cell.
+template <class Distance>
+void S2ClosestEdgeQueryBase<Distance>::ProcessEdges(const QueueEntry& entry) {
+ const S2ShapeIndexCell* index_cell = entry.index_cell;
+ for (int s = 0; s < index_cell->num_clipped(); ++s) {
+ const S2ClippedShape& clipped = index_cell->clipped(s);
+ const S2Shape* shape = index_->shape(clipped.shape_id());
+ for (int j = 0; j < clipped.num_edges(); ++j) {
+ MaybeAddResult(*shape, clipped.edge(j));
+ }
+ }
+}
+
+// Enqueue the given cell id.
+// REQUIRES: iter_ is positioned at a cell contained by "id".
+template <class Distance>
+inline void S2ClosestEdgeQueryBase<Distance>::ProcessOrEnqueue(
+ S2CellId id) {
+ S2_DCHECK(id.contains(iter_.id()));
+ if (iter_.id() == id) {
+ ProcessOrEnqueue(id, &iter_.cell());
+ } else {
+ ProcessOrEnqueue(id, nullptr);
+ }
+}
+
+// Add the given cell id to the queue. "index_cell" is the corresponding
+// S2ShapeIndexCell, or nullptr if "id" is not an index cell.
+//
+// This version is called directly only by InitQueue().
+template <class Distance>
+void S2ClosestEdgeQueryBase<Distance>::ProcessOrEnqueue(
+ S2CellId id, const S2ShapeIndexCell* index_cell) {
+ if (index_cell) {
+ // If this index cell has only a few edges, then it is faster to check
+ // them directly rather than computing the minimum distance to the S2Cell
+ // and inserting it into the queue.
+ static const int kMinEdgesToEnqueue = 10;
+ int num_edges = CountEdges(index_cell);
+ if (num_edges == 0) return;
+ if (num_edges < kMinEdgesToEnqueue) {
+ // Set "distance" to zero to avoid the expense of computing it.
+ ProcessEdges(QueueEntry(Distance::Zero(), id, index_cell));
+ return;
+ }
+ }
+ // Otherwise compute the minimum distance to any point in the cell and add
+ // it to the priority queue.
+ S2Cell cell(id);
+ Distance distance = distance_limit_;
+ if (!target_->UpdateMinDistance(cell, &distance)) return;
+ if (use_conservative_cell_distance_) {
+ // Ensure that "distance" is a lower bound on the true distance to the cell.
+ distance = distance - options().max_error(); // operator-=() not defined.
+ }
+ queue_.push(QueueEntry(distance, id, index_cell));
+}
+
+#endif // S2_S2CLOSEST_EDGE_QUERY_BASE_H_
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Common code for testing furthest/closest edge/point queries.
+
+#ifndef S2_S2CLOSEST_EDGE_QUERY_TESTING_H_
+#define S2_S2CLOSEST_EDGE_QUERY_TESTING_H_
+
+#include <vector>
+
+#include "s2/mutable_s2shape_index.h"
+#include "s2/s1angle.h"
+#include "s2/s2cap.h"
+#include "s2/s2cell.h"
+#include "s2/s2edge_distances.h"
+#include "s2/s2edge_vector_shape.h"
+#include "s2/s2loop.h"
+#include "s2/s2metrics.h"
+#include "s2/s2point.h"
+#include "s2/s2point_vector_shape.h"
+#include "s2/s2shapeutil_count_edges.h"
+#include "s2/s2shapeutil_shape_edge_id.h"
+#include "s2/s2testing.h"
+
+namespace s2testing {
+
+// An abstract class that adds edges to a MutableS2ShapeIndex for benchmarking.
+class ShapeIndexFactory {
+ public:
+ virtual ~ShapeIndexFactory() {}
+
+ // Requests that approximately "num_edges" edges located within the given
+ // S2Cap bound should be added to "index".
+ virtual void AddEdges(const S2Cap& index_cap, int num_edges,
+ MutableS2ShapeIndex* index) const = 0;
+};
+
+// Generates a regular loop that approximately fills the given S2Cap.
+//
+// Regular loops are nearly the worst case for distance calculations, since
+// many edges are nearly equidistant from any query point that is not
+// immediately adjacent to the loop.
+class RegularLoopShapeIndexFactory : public ShapeIndexFactory {
+ public:
+ void AddEdges(const S2Cap& index_cap, int num_edges,
+ MutableS2ShapeIndex* index) const override {
+ index->Add(absl::make_unique<S2Loop::OwningShape>(S2Loop::MakeRegularLoop(
+ index_cap.center(), index_cap.GetRadius(), num_edges)));
+ }
+};
+
+// Generates a fractal loop that approximately fills the given S2Cap.
+class FractalLoopShapeIndexFactory : public ShapeIndexFactory {
+ public:
+ void AddEdges(const S2Cap& index_cap, int num_edges,
+ MutableS2ShapeIndex* index) const override {
+ S2Testing::Fractal fractal;
+ fractal.SetLevelForApproxMaxEdges(num_edges);
+ index->Add(absl::make_unique<S2Loop::OwningShape>(
+ fractal.MakeLoop(S2Testing::GetRandomFrameAt(index_cap.center()),
+ index_cap.GetRadius())));
+ }
+};
+
+// Generates a cloud of points that approximately fills the given S2Cap.
+class PointCloudShapeIndexFactory : public ShapeIndexFactory {
+ public:
+ void AddEdges(const S2Cap& index_cap, int num_edges,
+ MutableS2ShapeIndex* index) const override {
+ std::vector<S2Point> points;
+ for (int i = 0; i < num_edges; ++i) {
+ points.push_back(S2Testing::SamplePoint(index_cap));
+ }
+ index->Add(absl::make_unique<S2PointVectorShape>(std::move(points)));
+ }
+};
+
+} // namespace s2testing
+#endif // S2_S2CLOSEST_EDGE_QUERY_TESTING_H_
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// See S2ClosestPointQuery (defined below) for an overview.
+
+#ifndef S2_S2CLOSEST_POINT_QUERY_H_
+#define S2_S2CLOSEST_POINT_QUERY_H_
+
+#include <vector>
+
+#include "s2/base/logging.h"
+#include "s2/s1angle.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s2closest_point_query_base.h"
+#include "s2/s2min_distance_targets.h"
+#include "s2/s2point_index.h"
+
+// Options that control the set of points returned. Note that by default
+// *all* points are returned, so you will always want to set either the
+// max_results() option or the max_distance() option (or both).
+//
+// This class is also available as S2ClosestPointQuery<Data>::Options.
+// (It is defined here to avoid depending on the "Data" template argument.)
+class S2ClosestPointQueryOptions :
+ public S2ClosestPointQueryBaseOptions<S2MinDistance> {
+ public:
+ using Distance = S2MinDistance;
+ using Base = S2ClosestPointQueryBaseOptions<Distance>;
+
+ // See S2ClosestPointQueryBaseOptions for the full set of options.
+
+ // Specifies that only points whose distance to the target is less than
+ // "max_distance" should be returned.
+ //
+ // Note that points whose distance is exactly equal to "max_distance" are
+ // not returned. Normally this doesn't matter, because distances are not
+ // computed exactly in the first place, but if such points are needed then
+ // see set_inclusive_max_distance() below.
+ //
+ // DEFAULT: Distance::Infinity()
+ void set_max_distance(S1ChordAngle max_distance);
+
+ // Like set_max_distance(), except that points whose distance is exactly
+ // equal to "max_distance" are also returned. Equivalent to calling
+ // set_max_distance(max_distance.Successor()).
+ void set_inclusive_max_distance(S1ChordAngle max_distance);
+
+ // Like set_inclusive_max_distance(), except that "max_distance" is also
+ // increased by the maximum error in the distance calculation. This ensures
+ // that all points whose true distance is less than or equal to
+ // "max_distance" will be returned (along with some points whose true
+ // distance is slightly greater).
+ //
+ // Algorithms that need to do exact distance comparisons can use this
+ // option to find a set of candidate points that can then be filtered
+ // further (e.g., using s2pred::CompareDistance).
+ void set_conservative_max_distance(S1ChordAngle max_distance);
+
+ // Versions of set_max_distance that take an S1Angle argument. (Note that
+ // these functions require a conversion, and that the S1ChordAngle versions
+ // are preferred.)
+ void set_max_distance(S1Angle max_distance);
+ void set_inclusive_max_distance(S1Angle max_distance);
+ void set_conservative_max_distance(S1Angle max_distance);
+
+ // See S2ClosestPointQueryBaseOptions for documentation.
+ using Base::set_max_error; // S1Chordangle version
+ void set_max_error(S1Angle max_error); // S1Angle version
+
+ // Inherited options (see s2closest_point_query_base.h for details):
+ using Base::set_max_results;
+ using Base::set_region;
+ using Base::set_use_brute_force;
+};
+
+// S2ClosestPointQueryTarget represents the geometry to which the distance is
+// measured. There are subtypes for measuring the distance to a point, an
+// edge, an S2Cell, or an S2ShapeIndex (an arbitrary collection of geometry).
+using S2ClosestPointQueryTarget = S2MinDistanceTarget;
+
+// Target subtype that computes the closest distance to a point.
+//
+// This class is also available as S2ClosestPointQuery<Data>::PointTarget.
+// (It is defined here to avoid depending on the "Data" template argument.)
+class S2ClosestPointQueryPointTarget final : public S2MinDistancePointTarget {
+ public:
+ explicit S2ClosestPointQueryPointTarget(const S2Point& point);
+ int max_brute_force_index_size() const override;
+};
+
+// Target subtype that computes the closest distance to an edge.
+//
+// This class is also available as S2ClosestPointQuery<Data>::EdgeTarget.
+// (It is defined here to avoid depending on the "Data" template argument.)
+class S2ClosestPointQueryEdgeTarget final : public S2MinDistanceEdgeTarget {
+ public:
+ explicit S2ClosestPointQueryEdgeTarget(const S2Point& a, const S2Point& b);
+ int max_brute_force_index_size() const override;
+};
+
+// Target subtype that computes the closest distance to an S2Cell
+// (including the interior of the cell).
+//
+// This class is also available as S2ClosestPointQuery<Data>::CellTarget.
+// (It is defined here to avoid depending on the "Data" template argument.)
+class S2ClosestPointQueryCellTarget final : public S2MinDistanceCellTarget {
+ public:
+ explicit S2ClosestPointQueryCellTarget(const S2Cell& cell);
+ int max_brute_force_index_size() const override;
+};
+
+// Target subtype that computes the closest distance to an S2ShapeIndex
+// (an arbitrary collection of points, polylines, and/or polygons).
+//
+// By default, distances are measured to the boundary and interior of
+// polygons in the S2ShapeIndex rather than to polygon boundaries only.
+// If you wish to change this behavior, you may call
+//
+// target.set_include_interiors(false);
+//
+// (see S2MinDistanceShapeIndexTarget for details).
+//
+// This class is also available as S2ClosestPointQuery<Data>::ShapeIndexTarget.
+// (It is defined here to avoid depending on the "Data" template argument.)
+class S2ClosestPointQueryShapeIndexTarget final :
+ public S2MinDistanceShapeIndexTarget {
+ public:
+ explicit S2ClosestPointQueryShapeIndexTarget(const S2ShapeIndex* index);
+ int max_brute_force_index_size() const override;
+};
+
+// Given a set of points stored in an S2PointIndex, S2ClosestPointQuery
+// provides methods that find the closest point(s) to a given query point
+// or query edge. Example usage:
+//
+// void Test(const vector<S2Point>& index_points,
+// const vector<S2Point>& target_points) {
+// // The template argument allows auxiliary data to be attached to each
+// // point (in this case, the array index).
+// S2PointIndex<int> index;
+// for (int i = 0; i < index_points.size(); ++i) {
+// index.Add(index_points[i], i);
+// }
+// S2ClosestPointQuery<int> query(&index);
+// query.mutable_options()->set_max_results(5);
+// for (const S2Point& target_point : target_points) {
+// S2ClosestPointQueryPointTarget target(target_point);
+// for (const auto& result : query.FindClosestPoints(&target)) {
+// // The Result class contains the following methods:
+// // distance() is the distance to the target.
+// // point() is the indexed point.
+// // data() is the auxiliary data.
+// DoSomething(target_point, result);
+// }
+// }
+// }
+//
+// You can find either the k closest points, or all points within a given
+// radius, or both (i.e., the k closest points up to a given maximum radius).
+// E.g. to find all the points within 5 kilometers, call
+//
+// query.mutable_options()->set_max_distance(
+// S2Earth::ToAngle(util::units::Kilometers(5)));
+//
+// By default *all* points are returned, so you should always specify either
+// max_results() or max_distance() or both. There is also a FindClosestPoint()
+// convenience method that returns only the closest point.
+//
+// You can restrict the results to an arbitrary S2Region, for example:
+//
+// S2LatLngRect rect(...);
+// query.mutable_options()->set_region(&rect); // Does *not* take ownership.
+//
+// To find the closest points to a query edge rather than a point, use:
+//
+// S2ClosestPointQueryEdgeTarget target(v0, v1);
+// query.FindClosestPoints(&target);
+//
+// Similarly you can find the closest points to an S2Cell by using an
+// S2ClosestPointQuery::CellTarget, and you can find the closest points to an
+// arbitrary collection of points, polylines, and polygons by using an
+// S2ClosestPointQuery::ShapeIndexTarget.
+//
+// The implementation is designed to be fast for both small and large
+// point sets.
+template <class Data>
+class S2ClosestPointQuery {
+ public:
+ // See S2ClosestPointQueryBase for full documentation.
+
+ using Index = S2PointIndex<Data>;
+ using PointData = typename Index::PointData;
+
+ // S2MinDistance is a thin wrapper around S1ChordAngle that implements the
+ // Distance concept required by S2ClosestPointQueryBase.
+ using Distance = S2MinDistance;
+ using Base = S2ClosestPointQueryBase<Distance, Data>;
+
+ // Each "Result" object represents a closest point. Here are its main
+ // methods (see S2ClosestPointQueryBase::Result for details):
+ //
+ // // The distance from the target to this point.
+ // S1ChordAngle distance() const;
+ //
+ // // The point itself.
+ // const S2Point& point() const;
+ //
+ // // The client-specified data associated with this point.
+ // const Data& data() const;
+ using Result = typename Base::Result;
+
+ using Options = S2ClosestPointQueryOptions;
+
+ // The available target types (see definitions above).
+ using Target = S2ClosestPointQueryTarget;
+ using PointTarget = S2ClosestPointQueryPointTarget;
+ using EdgeTarget = S2ClosestPointQueryEdgeTarget;
+ using CellTarget = S2ClosestPointQueryCellTarget;
+ using ShapeIndexTarget = S2ClosestPointQueryShapeIndexTarget;
+
+ // Convenience constructor that calls Init(). Options may be specified here
+ // or changed at any time using the mutable_options() accessor method.
+ explicit S2ClosestPointQuery(const Index* index,
+ const Options& options = Options());
+
+ // Default constructor; requires Init() to be called.
+ S2ClosestPointQuery();
+ ~S2ClosestPointQuery();
+
+ // Initializes the query. Options may be specified here or changed at any
+ // time using the mutable_options() accessor method.
+ //
+ // REQUIRES: "index" must persist for the lifetime of this object.
+ // REQUIRES: ReInit() must be called if "index" is modified.
+ void Init(const Index* index, const Options& options = Options());
+
+ // Reinitializes the query. This method must be called whenever the
+ // underlying index is modified.
+ void ReInit();
+
+ // Returns a reference to the underlying S2PointIndex.
+ const Index& index() const;
+
+ // Returns the query options. Options can be modifed between queries.
+ const Options& options() const;
+ Options* mutable_options();
+
+ // Returns the closest points to the given target that satisfy the current
+ // options. This method may be called multiple times.
+ std::vector<Result> FindClosestPoints(Target* target);
+
+ // This version can be more efficient when this method is called many times,
+ // since it does not require allocating a new vector on each call.
+ void FindClosestPoints(Target* target, std::vector<Result>* results);
+
+ //////////////////////// Convenience Methods ////////////////////////
+
+ // Returns the closest point to the target. If no point satisfies the search
+ // criteria, then a Result object with distance() == Infinity() and
+ // is_empty() == true is returned.
+ Result FindClosestPoint(Target* target);
+
+ // Returns the minimum distance to the target. If the index or target is
+ // empty, returns S1ChordAngle::Infinity().
+ //
+ // Use IsDistanceLess() if you only want to compare the distance against a
+ // threshold value, since it is often much faster.
+ S1ChordAngle GetDistance(Target* target);
+
+ // Returns true if the distance to "target" is less than "limit".
+ //
+ // This method is usually much faster than GetDistance(), since it is much
+ // less work to determine whether the minimum distance is above or below a
+ // threshold than it is to calculate the actual minimum distance.
+ bool IsDistanceLess(Target* target, S1ChordAngle limit);
+
+ // Like IsDistanceLess(), but also returns true if the distance to "target"
+ // is exactly equal to "limit".
+ bool IsDistanceLessOrEqual(Target* target, S1ChordAngle limit);
+
+ // Like IsDistanceLessOrEqual(), except that "limit" is increased by the
+ // maximum error in the distance calculation. This ensures that this
+ // function returns true whenever the true, exact distance is less than
+ // or equal to "limit".
+ //
+ // For example, suppose that we want to test whether two geometries might
+ // intersect each other after they are snapped together using S2Builder
+ // (using the IdentitySnapFunction with a given "snap_radius"). Since
+ // S2Builder uses exact distance predicates (s2predicates.h), we need to
+ // measure the distance between the two geometries conservatively. If the
+ // distance is definitely greater than "snap_radius", then the geometries
+ // are guaranteed to not intersect after snapping.
+ bool IsConservativeDistanceLessOrEqual(Target* target, S1ChordAngle limit);
+
+ private:
+ Options options_;
+ Base base_;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline void S2ClosestPointQueryOptions::set_max_distance(
+ S1ChordAngle max_distance) {
+ Base::set_max_distance(Distance(max_distance));
+}
+
+inline void S2ClosestPointQueryOptions::set_max_distance(S1Angle max_distance) {
+ Base::set_max_distance(Distance(max_distance));
+}
+
+inline void S2ClosestPointQueryOptions::set_inclusive_max_distance(
+ S1ChordAngle max_distance) {
+ set_max_distance(max_distance.Successor());
+}
+
+inline void S2ClosestPointQueryOptions::set_inclusive_max_distance(
+ S1Angle max_distance) {
+ set_inclusive_max_distance(S1ChordAngle(max_distance));
+}
+
+inline void S2ClosestPointQueryOptions::set_max_error(S1Angle max_error) {
+ Base::set_max_error(S1ChordAngle(max_error));
+}
+
+inline S2ClosestPointQueryPointTarget::S2ClosestPointQueryPointTarget(
+ const S2Point& point)
+ : S2MinDistancePointTarget(point) {
+}
+
+inline S2ClosestPointQueryEdgeTarget::S2ClosestPointQueryEdgeTarget(
+ const S2Point& a, const S2Point& b)
+ : S2MinDistanceEdgeTarget(a, b) {
+}
+
+inline S2ClosestPointQueryCellTarget::S2ClosestPointQueryCellTarget(
+ const S2Cell& cell)
+ : S2MinDistanceCellTarget(cell) {
+}
+
+inline S2ClosestPointQueryShapeIndexTarget::S2ClosestPointQueryShapeIndexTarget(
+ const S2ShapeIndex* index)
+ : S2MinDistanceShapeIndexTarget(index) {
+}
+
+template <class Data>
+inline S2ClosestPointQuery<Data>::S2ClosestPointQuery(const Index* index,
+ const Options& options) {
+ Init(index, options);
+}
+
+template <class Data>
+S2ClosestPointQuery<Data>::S2ClosestPointQuery() {
+ // Prevent inline constructor bloat by defining here.
+}
+
+template <class Data>
+S2ClosestPointQuery<Data>::~S2ClosestPointQuery() {
+ // Prevent inline destructor bloat by defining here.
+}
+
+template <class Data>
+void S2ClosestPointQuery<Data>::Init(const Index* index,
+ const Options& options) {
+ options_ = options;
+ base_.Init(index);
+}
+
+template <class Data>
+inline void S2ClosestPointQuery<Data>::ReInit() {
+ base_.ReInit();
+}
+
+template <class Data>
+inline const S2PointIndex<Data>& S2ClosestPointQuery<Data>::index() const {
+ return base_.index();
+}
+
+template <class Data>
+inline const S2ClosestPointQueryOptions& S2ClosestPointQuery<Data>::options()
+ const {
+ return options_;
+}
+
+template <class Data>
+inline S2ClosestPointQueryOptions*
+S2ClosestPointQuery<Data>::mutable_options() {
+ return &options_;
+}
+
+template <class Data>
+inline std::vector<typename S2ClosestPointQuery<Data>::Result>
+S2ClosestPointQuery<Data>::FindClosestPoints(Target* target) {
+ return base_.FindClosestPoints(target, options_);
+}
+
+template <class Data>
+inline void S2ClosestPointQuery<Data>::FindClosestPoints(
+ Target* target, std::vector<Result>* results) {
+ base_.FindClosestPoints(target, options_, results);
+}
+
+template <class Data>
+inline typename S2ClosestPointQuery<Data>::Result
+S2ClosestPointQuery<Data>::FindClosestPoint(Target* target) {
+ static_assert(sizeof(Options) <= 32, "Consider not copying Options here");
+ Options tmp_options = options_;
+ tmp_options.set_max_results(1);
+ return base_.FindClosestPoint(target, tmp_options);
+}
+
+template <class Data>
+inline S1ChordAngle S2ClosestPointQuery<Data>::GetDistance(Target* target) {
+ return FindClosestPoint(target).distance();
+}
+
+template <class Data>
+bool S2ClosestPointQuery<Data>::IsDistanceLess(
+ Target* target, S1ChordAngle limit) {
+ static_assert(sizeof(Options) <= 32, "Consider not copying Options here");
+ Options tmp_options = options_;
+ tmp_options.set_max_results(1);
+ tmp_options.set_max_distance(limit);
+ tmp_options.set_max_error(S1ChordAngle::Straight());
+ return !base_.FindClosestPoint(target, tmp_options).is_empty();
+}
+
+template <class Data>
+bool S2ClosestPointQuery<Data>::IsDistanceLessOrEqual(
+ Target* target, S1ChordAngle limit) {
+ static_assert(sizeof(Options) <= 32, "Consider not copying Options here");
+ Options tmp_options = options_;
+ tmp_options.set_max_results(1);
+ tmp_options.set_inclusive_max_distance(limit);
+ tmp_options.set_max_error(S1ChordAngle::Straight());
+ return !base_.FindClosestPoint(target, tmp_options).is_empty();
+}
+
+template <class Data>
+bool S2ClosestPointQuery<Data>::IsConservativeDistanceLessOrEqual(
+ Target* target, S1ChordAngle limit) {
+ static_assert(sizeof(Options) <= 32, "Consider not copying Options here");
+ Options tmp_options = options_;
+ tmp_options.set_max_results(1);
+ tmp_options.set_conservative_max_distance(limit);
+ tmp_options.set_max_error(S1ChordAngle::Straight());
+ return !base_.FindClosestPoint(target, tmp_options).is_empty();
+}
+
+#endif // S2_S2CLOSEST_POINT_QUERY_H_
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// See S2ClosestPointQueryBase (defined below) for an overview.
+
+#ifndef S2_S2CLOSEST_POINT_QUERY_BASE_H_
+#define S2_S2CLOSEST_POINT_QUERY_BASE_H_
+
+#include <vector>
+
+#include "s2/base/logging.h"
+#include "s2/third_party/absl/container/inlined_vector.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s2cap.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2cell_union.h"
+#include "s2/s2distance_target.h"
+#include "s2/s2edge_distances.h"
+#include "s2/s2point_index.h"
+#include "s2/s2region_coverer.h"
+
+// Options that control the set of points returned. Note that by default
+// *all* points are returned, so you will always want to set either the
+// max_results() option or the max_distance() option (or both).
+//
+// This class is also available as S2ClosestPointQueryBase<Data>::Options.
+// (It is defined here to avoid depending on the "Data" template argument.)
+//
+// The Distance template argument is described below.
+template <class Distance>
+class S2ClosestPointQueryBaseOptions {
+ public:
+ using Delta = typename Distance::Delta;
+
+ S2ClosestPointQueryBaseOptions();
+
+ // Specifies that at most "max_results" points should be returned.
+ //
+ // REQUIRES: max_results >= 1
+ // DEFAULT: numeric_limits<int>::max()
+ int max_results() const;
+ void set_max_results(int max_results);
+ static constexpr int kMaxMaxResults = std::numeric_limits<int>::max();
+
+ // Specifies that only points whose distance to the target is less than
+ // "max_distance" should be returned.
+ //
+ // Note that points whose distance is exactly equal to "max_distance" are
+ // not returned. In most cases this doesn't matter (since distances are
+ // not computed exactly in the first place), but if such points are needed
+ // then you can retrieve them by specifying "max_distance" as the next
+ // largest representable Distance. For example, if Distance is an
+ // S1ChordAngle then you can specify max_distance.Successor().
+ //
+ // DEFAULT: Distance::Infinity()
+ Distance max_distance() const;
+ void set_max_distance(Distance max_distance);
+
+ // Specifies that points up to max_error() further away than the true
+ // closest points may be substituted in the result set, as long as such
+ // points satisfy all the remaining search criteria (such as max_distance).
+ // This option only has an effect if max_results() is also specified;
+ // otherwise all points closer than max_distance() will always be returned.
+ //
+ // Note that this does not affect how the distance between points is
+ // computed; it simply gives the algorithm permission to stop the search
+ // early as soon as the best possible improvement drops below max_error().
+ //
+ // This can be used to implement distance predicates efficiently. For
+ // example, to determine whether the minimum distance is less than D, the
+ // IsDistanceLess() method sets max_results() == 1 and max_distance() ==
+ // max_error() == D. This causes the algorithm to terminate as soon as it
+ // finds any point whose distance is less than D, rather than continuing to
+ // search for a point that is even closer.
+ //
+ // DEFAULT: Distance::Delta::Zero()
+ Delta max_error() const;
+ void set_max_error(Delta max_error);
+
+ // Specifies that points must be contained by the given S2Region. "region"
+ // is owned by the caller and must persist during the lifetime of this
+ // object. The value may be changed between calls to FindClosestPoints(),
+ // or reset by calling set_region(nullptr).
+ //
+ // Note that if you want to set the region to a disc around a target point,
+ // it is faster to use a PointTarget with set_max_distance() instead. You
+ // can also call both methods, e.g. to set a maximum distance and also
+ // require that points lie within a given rectangle.
+ const S2Region* region() const;
+ void set_region(const S2Region* region);
+
+ // Specifies that distances should be computed by examining every point
+ // rather than using the S2ShapeIndex. This is useful for testing,
+ // benchmarking, and debugging.
+ //
+ // DEFAULT: false
+ bool use_brute_force() const;
+ void set_use_brute_force(bool use_brute_force);
+
+ private:
+ Distance max_distance_ = Distance::Infinity();
+ Delta max_error_ = Delta::Zero();
+ const S2Region* region_ = nullptr;
+ int max_results_ = kMaxMaxResults;
+ bool use_brute_force_ = false;
+};
+
+// S2ClosestPointQueryBase is a templatized class for finding the closest
+// point(s) to a given target. It is not intended to be used directly, but
+// rather to serve as the implementation of various specialized classes with
+// more convenient APIs (such as S2ClosestPointQuery). It is flexible enough
+// so that it can be adapted to compute maximum distances and even potentially
+// Hausdorff distances.
+//
+// By using the appropriate options, this class can answer questions such as:
+//
+// - Find the minimum distance between a point collection A and a target B.
+// - Find all points in collection A that are within a distance D of target B.
+// - Find the k points of collection A that are closest to a given point P.
+//
+// The target is any class that implements the S2DistanceTarget interface.
+// There are predefined targets for points, edges, S2Cells, and S2ShapeIndexes
+// (arbitrary collctions of points, polylines, and polygons).
+//
+// The Distance template argument is used to represent distances. Usually it
+// is a thin wrapper around S1ChordAngle, but another distance type may be
+// used as long as it implements the Distance concept described in
+// s2distance_targets.h. For example this can be used to measure maximum
+// distances, to get more accuracy, or to measure non-spheroidal distances.
+template <class Distance, class Data>
+class S2ClosestPointQueryBase {
+ public:
+ using Delta = typename Distance::Delta;
+ using Index = S2PointIndex<Data>;
+ using PointData = typename Index::PointData;
+ using Options = S2ClosestPointQueryBaseOptions<Distance>;
+
+ // The Target class represents the geometry to which the distance is
+ // measured. For example, there can be subtypes for measuring the distance
+ // to a point, an edge, or to an S2ShapeIndex (an arbitrary collection of
+ // geometry).
+ //
+ // Implementations do *not* need to be thread-safe. They may cache data or
+ // allocate temporary data structures in order to improve performance.
+ using Target = S2DistanceTarget<Distance>;
+
+ // Each "Result" object represents a closest point.
+ class Result {
+ public:
+ // The default constructor creates an "empty" result, with a distance() of
+ // Infinity() and non-dereferencable point() and data() values.
+ Result() : distance_(Distance::Infinity()), point_data_(nullptr) {}
+
+ // Constructs a Result object for the given point.
+ Result(Distance distance, const PointData* point_data)
+ : distance_(distance), point_data_(point_data) {}
+
+ // Returns true if this Result object does not refer to any data point.
+ // (The only case where an empty Result is returned is when the
+ // FindClosestPoint() method does not find any points that meet the
+ // specified criteria.)
+ bool is_empty() const { return point_data_ == nullptr; }
+
+ // The distance from the target to this point.
+ Distance distance() const { return distance_; }
+
+ // The point itself.
+ const S2Point& point() const { return point_data_->point(); }
+
+ // The client-specified data associated with this point.
+ const Data& data() const { return point_data_->data(); }
+
+ // Returns true if two Result objects are identical.
+ friend bool operator==(const Result& x, const Result& y) {
+ return (x.distance_ == y.distance_ && x.point_data_ == y.point_data_);
+ }
+
+ // Compares two Result objects first by distance, then by point_data().
+ friend bool operator<(const Result& x, const Result& y) {
+ if (x.distance_ < y.distance_) return true;
+ if (y.distance_ < x.distance_) return false;
+ return x.point_data_ < y.point_data_;
+ }
+
+ private:
+ Distance distance_;
+ const PointData* point_data_;
+ };
+
+ // The minimum number of points that a cell must contain to enqueue it
+ // rather than processing its contents immediately.
+ static constexpr int kMinPointsToEnqueue = 13;
+
+ // Default constructor; requires Init() to be called.
+ S2ClosestPointQueryBase();
+ ~S2ClosestPointQueryBase();
+
+ // Convenience constructor that calls Init().
+ explicit S2ClosestPointQueryBase(const Index* index);
+
+ // S2ClosestPointQueryBase is not copyable.
+ S2ClosestPointQueryBase(const S2ClosestPointQueryBase&) = delete;
+ void operator=(const S2ClosestPointQueryBase&) = delete;
+
+ // Initializes the query.
+ // REQUIRES: ReInit() must be called if "index" is modified.
+ void Init(const Index* index);
+
+ // Reinitializes the query. This method must be called whenever the
+ // underlying index is modified.
+ void ReInit();
+
+ // Return a reference to the underlying S2PointIndex.
+ const Index& index() const;
+
+ // Returns the closest points to the given target that satisfy the given
+ // options. This method may be called multiple times.
+ std::vector<Result> FindClosestPoints(Target* target, const Options& options);
+
+ // This version can be more efficient when this method is called many times,
+ // since it does not require allocating a new vector on each call.
+ void FindClosestPoints(Target* target, const Options& options,
+ std::vector<Result>* results);
+
+ // Convenience method that returns exactly one point. If no points satisfy
+ // the given search criteria, then a Result with distance() == Infinity()
+ // and is_empty() == true is returned.
+ //
+ // REQUIRES: options.max_results() == 1
+ Result FindClosestPoint(Target* target, const Options& options);
+
+ private:
+ using Iterator = typename Index::Iterator;
+
+ const Options& options() const { return *options_; }
+ void FindClosestPointsInternal(Target* target, const Options& options);
+ void FindClosestPointsBruteForce();
+ void FindClosestPointsOptimized();
+ void InitQueue();
+ void InitCovering();
+ void AddInitialRange(S2CellId first_id, S2CellId last_id);
+ void MaybeAddResult(const PointData* point_data);
+ bool ProcessOrEnqueue(S2CellId id, Iterator* iter, bool seek);
+
+ const Index* index_;
+ const Options* options_;
+ Target* target_;
+
+ // True if max_error() must be subtracted from priority queue cell distances
+ // in order to ensure that such distances are measured conservatively. This
+ // is true only if the target takes advantage of max_error() in order to
+ // return faster results, and 0 < max_error() < distance_limit_.
+ bool use_conservative_cell_distance_;
+
+ // For the optimized algorihm we precompute the top-level S2CellIds that
+ // will be added to the priority queue. There can be at most 6 of these
+ // cells. Essentially this is just a covering of the indexed points.
+ std::vector<S2CellId> index_covering_;
+
+ // The distance beyond which we can safely ignore further candidate points.
+ // (Candidates that are exactly at the limit are ignored; this is more
+ // efficient for UpdateMinDistance() and should not affect clients since
+ // distance measurements have a small amount of error anyway.)
+ //
+ // Initially this is the same as the maximum distance specified by the user,
+ // but it can also be updated by the algorithm (see MaybeAddResult).
+ Distance distance_limit_;
+
+ // The current result set is stored in one of three ways:
+ //
+ // - If max_results() == 1, the best result is kept in result_singleton_.
+ //
+ // - If max_results() == "infinity", results are appended to result_vector_
+ // and sorted/uniqued at the end.
+ //
+ // - Otherwise results are kept in a priority queue so that we can
+ // progressively reduce the distance limit once max_results() results
+ // have been found.
+ Result result_singleton_;
+ std::vector<Result> result_vector_;
+ std::priority_queue<Result, absl::InlinedVector<Result, 16>> result_set_;
+
+ // The algorithm maintains a priority queue of unprocessed S2CellIds, sorted
+ // in increasing order of distance from the target.
+ struct QueueEntry {
+ // A lower bound on the distance from the target to "id". This is the key
+ // of the priority queue.
+ Distance distance;
+
+ // The cell being queued.
+ S2CellId id;
+
+ QueueEntry(Distance _distance, S2CellId _id)
+ : distance(_distance), id(_id) {}
+
+ bool operator<(const QueueEntry& other) const {
+ // The priority queue returns the largest elements first, so we want the
+ // "largest" entry to have the smallest distance.
+ return other.distance < distance;
+ }
+ };
+ using CellQueue =
+ std::priority_queue<QueueEntry, absl::InlinedVector<QueueEntry, 16>>;
+ CellQueue queue_;
+
+ // Temporaries, defined here to avoid multiple allocations / initializations.
+
+ Iterator iter_;
+ std::vector<S2CellId> region_covering_;
+ std::vector<S2CellId> max_distance_covering_;
+ std::vector<S2CellId> intersection_with_region_;
+ std::vector<S2CellId> intersection_with_max_distance_;
+ const PointData* tmp_point_data_[kMinPointsToEnqueue - 1];
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+template <class Distance> inline
+S2ClosestPointQueryBaseOptions<Distance>::S2ClosestPointQueryBaseOptions() {
+}
+
+template <class Distance>
+inline int S2ClosestPointQueryBaseOptions<Distance>::max_results() const {
+ return max_results_;
+}
+
+template <class Distance>
+inline void S2ClosestPointQueryBaseOptions<Distance>::set_max_results(
+ int max_results) {
+ S2_DCHECK_GE(max_results, 1);
+ max_results_ = max_results;
+}
+
+template <class Distance>
+inline Distance S2ClosestPointQueryBaseOptions<Distance>::max_distance()
+ const {
+ return max_distance_;
+}
+
+template <class Distance>
+inline void S2ClosestPointQueryBaseOptions<Distance>::set_max_distance(
+ Distance max_distance) {
+ max_distance_ = max_distance;
+}
+
+template <class Distance>
+inline typename Distance::Delta
+S2ClosestPointQueryBaseOptions<Distance>::max_error() const {
+ return max_error_;
+}
+
+template <class Distance>
+inline void S2ClosestPointQueryBaseOptions<Distance>::set_max_error(
+ Delta max_error) {
+ max_error_ = max_error;
+}
+
+template <class Distance>
+inline const S2Region* S2ClosestPointQueryBaseOptions<Distance>::region()
+ const {
+ return region_;
+}
+
+template <class Distance>
+inline void S2ClosestPointQueryBaseOptions<Distance>::set_region(
+ const S2Region* region) {
+ region_ = region;
+}
+
+template <class Distance>
+inline bool S2ClosestPointQueryBaseOptions<Distance>::use_brute_force() const {
+ return use_brute_force_;
+}
+
+template <class Distance>
+inline void S2ClosestPointQueryBaseOptions<Distance>::set_use_brute_force(
+ bool use_brute_force) {
+ use_brute_force_ = use_brute_force;
+}
+
+template <class Distance, class Data>
+S2ClosestPointQueryBase<Distance, Data>::S2ClosestPointQueryBase() {
+}
+
+template <class Distance, class Data>
+S2ClosestPointQueryBase<Distance, Data>::~S2ClosestPointQueryBase() {
+ // Prevent inline destructor bloat by providing a definition.
+}
+
+template <class Distance, class Data>
+inline S2ClosestPointQueryBase<Distance, Data>::S2ClosestPointQueryBase(
+ const S2PointIndex<Data>* index) : S2ClosestPointQueryBase() {
+ Init(index);
+}
+
+template <class Distance, class Data>
+void S2ClosestPointQueryBase<Distance, Data>::Init(
+ const S2PointIndex<Data>* index) {
+ index_ = index;
+ ReInit();
+}
+
+template <class Distance, class Data>
+void S2ClosestPointQueryBase<Distance, Data>::ReInit() {
+ iter_.Init(index_);
+ index_covering_.clear();
+}
+
+template <class Distance, class Data>
+inline const S2PointIndex<Data>&
+S2ClosestPointQueryBase<Distance, Data>::index() const {
+ return *index_;
+}
+
+template <class Distance, class Data>
+inline std::vector<typename S2ClosestPointQueryBase<Distance, Data>::Result>
+S2ClosestPointQueryBase<Distance, Data>::FindClosestPoints(
+ Target* target, const Options& options) {
+ std::vector<Result> results;
+ FindClosestPoints(target, options, &results);
+ return results;
+}
+
+template <class Distance, class Data>
+typename S2ClosestPointQueryBase<Distance, Data>::Result
+S2ClosestPointQueryBase<Distance, Data>::FindClosestPoint(
+ Target* target, const Options& options) {
+ S2_DCHECK_EQ(options.max_results(), 1);
+ FindClosestPointsInternal(target, options);
+ return result_singleton_;
+}
+
+template <class Distance, class Data>
+void S2ClosestPointQueryBase<Distance, Data>::FindClosestPoints(
+ Target* target, const Options& options, std::vector<Result>* results) {
+ FindClosestPointsInternal(target, options);
+ results->clear();
+ if (options.max_results() == 1) {
+ if (!result_singleton_.is_empty()) {
+ results->push_back(result_singleton_);
+ }
+ } else if (options.max_results() == Options::kMaxMaxResults) {
+ std::sort(result_vector_.begin(), result_vector_.end());
+ std::unique_copy(result_vector_.begin(), result_vector_.end(),
+ std::back_inserter(*results));
+ result_vector_.clear();
+ } else {
+ results->reserve(result_set_.size());
+ for (; !result_set_.empty(); result_set_.pop()) {
+ results->push_back(result_set_.top());
+ }
+ // The priority queue returns the largest elements first.
+ std::reverse(results->begin(), results->end());
+ S2_DCHECK(std::is_sorted(results->begin(), results->end()));
+ }
+}
+
+template <class Distance, class Data>
+void S2ClosestPointQueryBase<Distance, Data>::FindClosestPointsInternal(
+ Target* target, const Options& options) {
+ target_ = target;
+ options_ = &options;
+
+ distance_limit_ = options.max_distance();
+ result_singleton_ = Result();
+ S2_DCHECK(result_vector_.empty());
+ S2_DCHECK(result_set_.empty());
+ S2_DCHECK_GE(target->max_brute_force_index_size(), 0);
+ if (distance_limit_ == Distance::Zero()) return;
+
+ if (options.max_results() == Options::kMaxMaxResults &&
+ options.max_distance() == Distance::Infinity() &&
+ options.region() == nullptr) {
+ S2_LOG(WARNING) << "Returning all points "
+ "(max_results/max_distance/region not set)";
+ }
+
+ // If max_error() > 0 and the target takes advantage of this, then we may
+ // need to adjust the distance estimates to the priority queue cells to
+ // ensure that they are always a lower bound on the true distance. For
+ // example, suppose max_distance == 100, max_error == 30, and we compute the
+ // distance to the target from some cell C0 as d(C0) == 80. Then because
+ // the target takes advantage of max_error(), the true distance could be as
+ // low as 50. In order not to miss edges contained by such cells, we need
+ // to subtract max_error() from the distance estimates. This behavior is
+ // controlled by the use_conservative_cell_distance_ flag.
+ //
+ // However there is one important case where this adjustment is not
+ // necessary, namely when max_distance() < max_error(). This is because
+ // max_error() only affects the algorithm once at least max_results() edges
+ // have been found that satisfy the given distance limit. At that point,
+ // max_error() is subtracted from distance_limit_ in order to ensure that
+ // any further matches are closer by at least that amount. But when
+ // max_distance() < max_error(), this reduces the distance limit to 0,
+ // i.e. all remaining candidate cells and edges can safely be discarded.
+ // (Note that this is how IsDistanceLess() and friends are implemented.)
+ //
+ // Note that Distance::Delta only supports operator==.
+ bool target_uses_max_error = (!(options.max_error() == Delta::Zero()) &&
+ target_->set_max_error(options.max_error()));
+
+ // Note that we can't compare max_error() and distance_limit_ directly
+ // because one is a Delta and one is a Distance. Instead we subtract them.
+ use_conservative_cell_distance_ = target_uses_max_error &&
+ (distance_limit_ == Distance::Infinity() ||
+ Distance::Zero() < distance_limit_ - options.max_error());
+
+ // Note that given point is processed only once (unlike S2ClosestEdgeQuery),
+ // and therefore we don't need to worry about the possibility of having
+ // duplicate points in the results.
+ if (options.use_brute_force() ||
+ index_->num_points() <= target_->max_brute_force_index_size()) {
+ FindClosestPointsBruteForce();
+ } else {
+ FindClosestPointsOptimized();
+ }
+}
+
+template <class Distance, class Data>
+void S2ClosestPointQueryBase<Distance, Data>::FindClosestPointsBruteForce() {
+ for (iter_.Begin(); !iter_.done(); iter_.Next()) {
+ MaybeAddResult(&iter_.point_data());
+ }
+}
+
+template <class Distance, class Data>
+void S2ClosestPointQueryBase<Distance, Data>::FindClosestPointsOptimized() {
+ InitQueue();
+ while (!queue_.empty()) {
+ // We need to copy the top entry before removing it, and we need to remove
+ // it before adding any new entries to the queue.
+ QueueEntry entry = queue_.top();
+ queue_.pop();
+ // Work around weird parse error in gcc 4.9 by using a local variable for
+ // entry.distance.
+ Distance distance = entry.distance;
+ if (!(distance < distance_limit_)) {
+ queue_ = CellQueue(); // Clear any remaining entries.
+ break;
+ }
+ S2CellId child = entry.id.child_begin();
+ // We already know that it has too many points, so process its children.
+ // Each child may either be processed directly or enqueued again. The
+ // loop is optimized so that we don't seek unnecessarily.
+ bool seek = true;
+ for (int i = 0; i < 4; ++i, child = child.next()) {
+ seek = ProcessOrEnqueue(child, &iter_, seek);
+ }
+ }
+}
+
+template <class Distance, class Data>
+void S2ClosestPointQueryBase<Distance, Data>::InitQueue() {
+ S2_DCHECK(queue_.empty());
+
+ // Optimization: rather than starting with the entire index, see if we can
+ // limit the search region to a small disc. Then we can find a covering for
+ // that disc and intersect it with the covering for the index. This can
+ // save a lot of work when the search region is small.
+ S2Cap cap = target_->GetCapBound();
+ if (cap.is_empty()) return; // Empty target.
+ if (options().max_results() == 1) {
+ // If the user is searching for just the closest point, we can compute an
+ // upper bound on search radius by seeking to the center of the target's
+ // bounding cap and looking at the adjacent index points (in S2CellId
+ // order). The minimum distance to either of these points is an upper
+ // bound on the search radius.
+ //
+ // TODO(ericv): The same strategy would also work for small values of
+ // max_results() > 1, e.g. max_results() == 20, except that we would need to
+ // examine more neighbors (at least 20, and preferably 20 in each
+ // direction). It's not clear whether this is a common case, though, and
+ // also this would require extending MaybeAddResult() so that it can
+ // remove duplicate entries. (The points added here may be re-added by
+ // ProcessOrEnqueue(), but this is okay when max_results() == 1.)
+ iter_.Seek(S2CellId(cap.center()));
+ if (!iter_.done()) {
+ MaybeAddResult(&iter_.point_data());
+ }
+ if (iter_.Prev()) {
+ MaybeAddResult(&iter_.point_data());
+ }
+ // Skip the rest of the algorithm if we found a matching point.
+ if (distance_limit_ == Distance::Zero()) return;
+ }
+ // We start with a covering of the set of indexed points, then intersect it
+ // with the given region (if any) and maximum search radius disc (if any).
+ if (index_covering_.empty()) InitCovering();
+ const std::vector<S2CellId>* initial_cells = &index_covering_;
+ if (options().region()) {
+ S2RegionCoverer coverer;
+ coverer.mutable_options()->set_max_cells(4);
+ coverer.GetCovering(*options().region(), ®ion_covering_);
+ S2CellUnion::GetIntersection(index_covering_, region_covering_,
+ &intersection_with_region_);
+ initial_cells = &intersection_with_region_;
+ }
+ if (distance_limit_ < Distance::Infinity()) {
+ S2RegionCoverer coverer;
+ coverer.mutable_options()->set_max_cells(4);
+ S1ChordAngle radius = cap.radius() + distance_limit_.GetChordAngleBound();
+ S2Cap search_cap(cap.center(), radius);
+ coverer.GetFastCovering(search_cap, &max_distance_covering_);
+ S2CellUnion::GetIntersection(*initial_cells, max_distance_covering_,
+ &intersection_with_max_distance_);
+ initial_cells = &intersection_with_max_distance_;
+ }
+ iter_.Begin();
+ for (int i = 0; i < initial_cells->size() && !iter_.done(); ++i) {
+ S2CellId id = (*initial_cells)[i];
+ ProcessOrEnqueue(id, &iter_, id.range_min() > iter_.id() /*seek*/);
+ }
+}
+
+template <class Distance, class Data>
+void S2ClosestPointQueryBase<Distance, Data>::InitCovering() {
+ // Compute the "index covering", which is a small number of S2CellIds that
+ // cover the indexed points. There are two cases:
+ //
+ // - If the index spans more than one face, then there is one covering cell
+ // per spanned face, just big enough to cover the index cells on that face.
+ //
+ // - If the index spans only one face, then we find the smallest cell "C"
+ // that covers the index cells on that face (just like the case above).
+ // Then for each of the 4 children of "C", if the child contains any index
+ // cells then we create a covering cell that is big enough to just fit
+ // those index cells (i.e., shrinking the child as much as possible to fit
+ // its contents). This essentially replicates what would happen if we
+ // started with "C" as the covering cell, since "C" would immediately be
+ // split, except that we take the time to prune the children further since
+ // this will save work on every subsequent query.
+ index_covering_.reserve(6);
+ iter_.Finish();
+ if (!iter_.Prev()) return; // Empty index.
+ S2CellId index_last_id = iter_.id();
+ iter_.Begin();
+ if (iter_.id() != index_last_id) {
+ // The index has at least two cells. Choose a level such that the entire
+ // index can be spanned with at most 6 cells (if the index spans multiple
+ // faces) or 4 cells (it the index spans a single face).
+ int level = iter_.id().GetCommonAncestorLevel(index_last_id) + 1;
+
+ // Visit each potential covering cell except the last (handled below).
+ S2CellId last_id = index_last_id.parent(level);
+ for (S2CellId id = iter_.id().parent(level);
+ id != last_id; id = id.next()) {
+ // Skip any covering cells that don't contain any index cells.
+ if (id.range_max() < iter_.id()) continue;
+
+ // Find the range of index cells contained by this covering cell and
+ // then shrink the cell if necessary so that it just covers them.
+ S2CellId cell_first_id = iter_.id();
+ iter_.Seek(id.range_max().next());
+ iter_.Prev();
+ S2CellId cell_last_id = iter_.id();
+ iter_.Next();
+ AddInitialRange(cell_first_id, cell_last_id);
+ }
+ }
+ AddInitialRange(iter_.id(), index_last_id);
+}
+
+// Adds a cell to index_covering_ that covers the given inclusive range.
+//
+// REQUIRES: "first" and "last" have a common ancestor.
+template <class Distance, class Data>
+void S2ClosestPointQueryBase<Distance, Data>::AddInitialRange(
+ S2CellId first_id, S2CellId last_id) {
+ // Add the lowest common ancestor of the given range.
+ int level = first_id.GetCommonAncestorLevel(last_id);
+ S2_DCHECK_GE(level, 0);
+ index_covering_.push_back(first_id.parent(level));
+}
+
+template <class Distance, class Data>
+void S2ClosestPointQueryBase<Distance, Data>::MaybeAddResult(
+ const PointData* point_data) {
+ Distance distance = distance_limit_;
+ if (!target_->UpdateMinDistance(point_data->point(), &distance)) return;
+
+ const S2Region* region = options().region();
+ if (region && !region->Contains(point_data->point())) return;
+
+ Result result(distance, point_data);
+ if (options().max_results() == 1) {
+ // Optimization for the common case where only the closest point is wanted.
+ result_singleton_ = result;
+ distance_limit_ = result.distance() - options().max_error();
+ } else if (options().max_results() == Options::kMaxMaxResults) {
+ result_vector_.push_back(result); // Sort/unique at end.
+ } else {
+ // Add this point to result_set_. Note that with the current algorithm
+ // each candidate point is considered at most once (except for one special
+ // case where max_results() == 1, see InitQueue for details), so we don't
+ // need to worry about possibly adding a duplicate entry here.
+ if (result_set_.size() >= options().max_results()) {
+ result_set_.pop(); // Replace the furthest result point.
+ }
+ result_set_.push(result);
+ if (result_set_.size() >= options().max_results()) {
+ distance_limit_ = result_set_.top().distance() - options().max_error();
+ }
+ }
+}
+
+// Either process the contents of the given cell immediately, or add it to the
+// queue to be subdivided. If "seek" is false, then "iter" must already be
+// positioned at the first indexed point within or after this cell.
+//
+// Returns "true" if the cell was added to the queue, and "false" if it was
+// processed immediately, in which case "iter" is left positioned at the next
+// cell in S2CellId order.
+template <class Distance, class Data>
+bool S2ClosestPointQueryBase<Distance, Data>::ProcessOrEnqueue(
+ S2CellId id, Iterator* iter, bool seek) {
+ if (seek) iter->Seek(id.range_min());
+ if (id.is_leaf()) {
+ // Leaf cells can't be subdivided.
+ for (; !iter->done() && iter->id() == id; iter->Next()) {
+ MaybeAddResult(&iter->point_data());
+ }
+ return false; // No need to seek to next child.
+ }
+ S2CellId last = id.range_max();
+ int num_points = 0;
+ for (; !iter->done() && iter->id() <= last; iter->Next()) {
+ if (num_points == kMinPointsToEnqueue - 1) {
+ // This cell has too many points (including this one), so enqueue it.
+ S2Cell cell(id);
+ Distance distance = distance_limit_;
+ // We check "region_" second because it may be relatively expensive.
+ if (target_->UpdateMinDistance(cell, &distance) &&
+ (!options().region() || options().region()->MayIntersect(cell))) {
+ if (use_conservative_cell_distance_) {
+ // Ensure that "distance" is a lower bound on distance to the cell.
+ distance = distance - options().max_error();
+ }
+ queue_.push(QueueEntry(distance, id));
+ }
+ return true; // Seek to next child.
+ }
+ tmp_point_data_[num_points++] = &iter->point_data();
+ }
+ // There were few enough points that we might as well process them now.
+ for (int i = 0; i < num_points; ++i) {
+ MaybeAddResult(tmp_point_data_[i]);
+ }
+ return false; // No need to seek to next child.
+}
+
+#endif // S2_S2CLOSEST_POINT_QUERY_BASE_H_
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2CONTAINS_POINT_QUERY_H_
+#define S2_S2CONTAINS_POINT_QUERY_H_
+
+#include <vector>
+
+#include "s2/s2edge_crosser.h"
+#include "s2/s2shape_index.h"
+#include "s2/s2shapeutil_shape_edge.h"
+
+// Defines whether shapes are considered to contain their vertices. Note that
+// these definitions differ from the ones used by S2BooleanOperation.
+//
+// - In the OPEN model, no shapes contain their vertices (not even points).
+// Therefore Contains(S2Point) returns true if and only if the point is
+// in the interior of some polygon.
+//
+// - In the SEMI_OPEN model, polygon point containment is defined such that
+// if several polygons tile the region around a vertex, then exactly one of
+// those polygons contains that vertex. Points and polylines still do not
+// contain any vertices.
+//
+// - In the CLOSED model, all shapes contain their vertices (including points
+// and polylines).
+//
+// Note that points other than vertices are never contained by polylines.
+// If you want need this behavior, use S2ClosestEdgeQuery::IsDistanceLess()
+// with a suitable distance threshold instead.
+enum class S2VertexModel { OPEN, SEMI_OPEN, CLOSED };
+
+// This class defines the options supported by S2ContainsPointQuery.
+class S2ContainsPointQueryOptions {
+ public:
+ S2ContainsPointQueryOptions() {}
+
+ // Convenience constructor that sets the vertex_model() option.
+ explicit S2ContainsPointQueryOptions(S2VertexModel vertex_model);
+
+ // Controls whether shapes are considered to contain their vertices (see
+ // definitions above). By default the SEMI_OPEN model is used.
+ //
+ // DEFAULT: S2VertexModel::SEMI_OPEN
+ S2VertexModel vertex_model() const;
+ void set_vertex_model(S2VertexModel model);
+
+ private:
+ S2VertexModel vertex_model_ = S2VertexModel::SEMI_OPEN;
+};
+
+// S2ContainsPointQuery determines whether one or more shapes in an
+// S2ShapeIndex contain a given S2Point. The S2ShapeIndex may contain any
+// number of points, polylines, and/or polygons (possibly overlapping).
+// Shape boundaries may be modeled as OPEN, SEMI_OPEN, or CLOSED (this affects
+// whether or not shapes are considered to contain their vertices).
+//
+// Example usage:
+// auto query = MakeS2ContainsPointQuery(&index, S2VertexModel::CLOSED);
+// return query.Contains(point);
+//
+// This class is not thread-safe. To use it in parallel, each thread should
+// construct its own instance (this is not expensive).
+//
+// However, note that if you need to do a large number of point containment
+// tests, it is more efficient to re-use the S2ContainsPointQuery object
+// rather than constructing a new one each time.
+template <class IndexType>
+class S2ContainsPointQuery {
+ private:
+ using Iterator = typename IndexType::Iterator;
+
+ public:
+ // Default constructor; requires Init() to be called.
+ S2ContainsPointQuery();
+
+ // Rather than calling this constructor, which requires specifying the
+ // IndexType template argument explicitly, the preferred idiom is to call
+ // MakeS2ContainsPointQuery() instead. For example:
+ //
+ // return MakeS2ContainsPointQuery(&index).Contains(p);
+ using Options = S2ContainsPointQueryOptions;
+ explicit S2ContainsPointQuery(const IndexType* index,
+ const Options& options = Options());
+
+ // Convenience constructor that accepts the S2VertexModel directly.
+ S2ContainsPointQuery(const IndexType* index, S2VertexModel vertex_model);
+
+ const IndexType& index() const { return *index_; }
+ const Options& options() const { return options_; }
+
+ // Equivalent to the two-argument constructor above.
+ void Init(const IndexType* index, const Options& options = Options());
+
+ // Returns true if any shape in the given index() contains the point "p"
+ // under the vertex model specified (OPEN, SEMI_OPEN, or CLOSED).
+ bool Contains(const S2Point& p);
+
+ // Returns true if the given shape contains the point "p" under the vertex
+ // model specified (OPEN, SEMI_OPEN, or CLOSED).
+ //
+ // REQUIRES: "shape" belongs to index().
+ bool ShapeContains(const S2Shape& shape, const S2Point& p);
+
+ // Visits all shapes in the given index() that contain the given point "p",
+ // terminating early if the given ShapeVisitor function returns false (in
+ // which case VisitContainingShapes returns false as well). Each shape is
+ // visited at most once.
+ //
+ // Note that the API allows non-const access to the visited shapes.
+ using ShapeVisitor = std::function<bool (S2Shape* shape)>;
+ bool VisitContainingShapes(const S2Point& p, const ShapeVisitor& visitor);
+
+ // Convenience function that returns all the shapes that contain the given
+ // point "p".
+ std::vector<S2Shape*> GetContainingShapes(const S2Point& p);
+
+ // Visits all edges in the given index() that are incident to the point "p"
+ // (i.e., "p" is one of the edge endpoints), terminating early if the given
+ // EdgeVisitor function returns false (in which case VisitIncidentEdges
+ // returns false as well). Each edge is visited at most once.
+ using EdgeVisitor = std::function<bool (const s2shapeutil::ShapeEdge&)>;
+ bool VisitIncidentEdges(const S2Point& p, const EdgeVisitor& visitor);
+
+ /////////////////////////// Low-Level Methods ////////////////////////////
+ //
+ // Most clients will not need the following methods. They can be slightly
+ // more efficient but are harder to use.
+
+ // Returns a pointer to the iterator used internally by this class, in order
+ // to avoid the need for clients to create their own iterator. Clients are
+ // allowed to reposition this iterator arbitrarily between method calls.
+ Iterator* mutable_iter() { return &it_; }
+
+ // Low-level helper method that returns true if the given S2ClippedShape
+ // referred to by an S2ShapeIndex::Iterator contains the point "p".
+ bool ShapeContains(const Iterator& it, const S2ClippedShape& clipped,
+ const S2Point& p) const;
+
+ private:
+ const IndexType* index_;
+ Options options_;
+ Iterator it_;
+};
+
+// Returns an S2ContainsPointQuery for the given S2ShapeIndex. Note that
+// it is efficient to return S2ContainsPointQuery objects by value.
+template <class IndexType>
+inline S2ContainsPointQuery<IndexType> MakeS2ContainsPointQuery(
+ const IndexType* index,
+ const S2ContainsPointQueryOptions& options =
+ S2ContainsPointQueryOptions()) {
+ return S2ContainsPointQuery<IndexType>(index, options);
+}
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline S2ContainsPointQueryOptions::S2ContainsPointQueryOptions(
+ S2VertexModel vertex_model)
+ : vertex_model_(vertex_model) {
+}
+
+inline S2VertexModel S2ContainsPointQueryOptions::vertex_model() const {
+ return vertex_model_;
+}
+
+inline void S2ContainsPointQueryOptions::set_vertex_model(S2VertexModel model) {
+ vertex_model_ = model;
+}
+
+template <class IndexType>
+inline S2ContainsPointQuery<IndexType>::S2ContainsPointQuery()
+ : index_(nullptr) {
+}
+
+template <class IndexType>
+inline S2ContainsPointQuery<IndexType>::S2ContainsPointQuery(
+ const IndexType* index, const Options& options)
+ : index_(index), options_(options), it_(index_) {
+}
+
+template <class IndexType>
+inline S2ContainsPointQuery<IndexType>::S2ContainsPointQuery(
+ const IndexType* index, S2VertexModel vertex_model)
+ : S2ContainsPointQuery(index, Options(vertex_model)) {
+}
+
+template <class IndexType>
+void S2ContainsPointQuery<IndexType>::Init(const IndexType* index,
+ const Options& options) {
+ index_ = index;
+ options_ = options;
+ it_.Init(index);
+}
+
+template <class IndexType>
+bool S2ContainsPointQuery<IndexType>::Contains(const S2Point& p) {
+ if (!it_.Locate(p)) return false;
+
+ const S2ShapeIndexCell& cell = it_.cell();
+ int num_clipped = cell.num_clipped();
+ for (int s = 0; s < num_clipped; ++s) {
+ if (ShapeContains(it_, cell.clipped(s), p)) return true;
+ }
+ return false;
+}
+
+template <class IndexType>
+bool S2ContainsPointQuery<IndexType>::ShapeContains(const S2Shape& shape,
+ const S2Point& p) {
+ if (!it_.Locate(p)) return false;
+ const S2ClippedShape* clipped = it_.cell().find_clipped(shape.id());
+ if (clipped == nullptr) return false;
+ return ShapeContains(it_, *clipped, p);
+}
+
+template <class IndexType>
+bool S2ContainsPointQuery<IndexType>::VisitContainingShapes(
+ const S2Point& p, const ShapeVisitor& visitor) {
+ // This function returns "false" only if the algorithm terminates early
+ // because the "visitor" function returned false.
+ if (!it_.Locate(p)) return true;
+
+ const S2ShapeIndexCell& cell = it_.cell();
+ int num_clipped = cell.num_clipped();
+ for (int s = 0; s < num_clipped; ++s) {
+ const S2ClippedShape& clipped = cell.clipped(s);
+ if (ShapeContains(it_, clipped, p) &&
+ !visitor(index_->shape(clipped.shape_id()))) {
+ return false;
+ }
+ }
+ return true;
+}
+
+template <class IndexType>
+bool S2ContainsPointQuery<IndexType>::VisitIncidentEdges(
+ const S2Point& p, const EdgeVisitor& visitor) {
+ // This function returns "false" only if the algorithm terminates early
+ // because the "visitor" function returned false.
+ if (!it_.Locate(p)) return true;
+
+ const S2ShapeIndexCell& cell = it_.cell();
+ int num_clipped = cell.num_clipped();
+ for (int s = 0; s < num_clipped; ++s) {
+ const S2ClippedShape& clipped = cell.clipped(s);
+ int num_edges = clipped.num_edges();
+ if (num_edges == 0) continue;
+ const S2Shape& shape = *index_->shape(clipped.shape_id());
+ for (int i = 0; i < num_edges; ++i) {
+ int edge_id = clipped.edge(i);
+ auto edge = shape.edge(edge_id);
+ if ((edge.v0 == p || edge.v1 == p) &&
+ !visitor(s2shapeutil::ShapeEdge(shape.id(), edge_id, edge))) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+template <class IndexType>
+std::vector<S2Shape*> S2ContainsPointQuery<IndexType>::GetContainingShapes(
+ const S2Point& p) {
+ std::vector<S2Shape*> results;
+ VisitContainingShapes(p, [&results](S2Shape* shape) {
+ results.push_back(shape);
+ return true;
+ });
+ return results;
+}
+
+template <class IndexType>
+bool S2ContainsPointQuery<IndexType>::ShapeContains(
+ const Iterator& it, const S2ClippedShape& clipped, const S2Point& p) const {
+ bool inside = clipped.contains_center();
+ const int num_edges = clipped.num_edges();
+ if (num_edges > 0) {
+ const S2Shape& shape = *index_->shape(clipped.shape_id());
+ if (shape.dimension() < 2) {
+ // Points and polylines can be ignored unless the vertex model is CLOSED.
+ if (options_.vertex_model() != S2VertexModel::CLOSED) return false;
+
+ // Otherwise, the point is contained if and only if it matches a vertex.
+ for (int i = 0; i < num_edges; ++i) {
+ auto edge = shape.edge(clipped.edge(i));
+ if (edge.v0 == p || edge.v1 == p) return true;
+ }
+ return false;
+ }
+ // Test containment by drawing a line segment from the cell center to the
+ // given point and counting edge crossings.
+ S2CopyingEdgeCrosser crosser(it.center(), p);
+ for (int i = 0; i < num_edges; ++i) {
+ auto edge = shape.edge(clipped.edge(i));
+ int sign = crosser.CrossingSign(edge.v0, edge.v1);
+ if (sign < 0) continue;
+ if (sign == 0) {
+ // For the OPEN and CLOSED models, check whether "p" is a vertex.
+ if (options_.vertex_model() != S2VertexModel::SEMI_OPEN &&
+ (edge.v0 == p || edge.v1 == p)) {
+ return (options_.vertex_model() == S2VertexModel::CLOSED);
+ }
+ sign = S2::VertexCrossing(crosser.a(), crosser.b(), edge.v0, edge.v1);
+ }
+ inside ^= sign;
+ }
+ }
+ return inside;
+}
+
+#endif // S2_S2CONTAINS_POINT_QUERY_H_
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2CONTAINS_VERTEX_QUERY_H_
+#define S2_S2CONTAINS_VERTEX_QUERY_H_
+
+#include "s2/util/gtl/btree_map.h"
+#include "s2/s2point.h"
+
+// This class determines whether a polygon contains one of its vertices given
+// the edges incident to that vertex. The result is +1 if the vertex is
+// contained, -1 if it is not contained, and 0 if the incident edges consist
+// of matched sibling pairs (in which case the result cannot be determined
+// locally).
+//
+// Point containment is defined according to the "semi-open" boundary model
+// (see S2VertexModel), which means that if several polygons tile the region
+// around a vertex, then exactly one of those polygons contains that vertex.
+//
+// This class is not thread-safe. To use it in parallel, each thread should
+// construct its own instance (this is not expensive).
+class S2ContainsVertexQuery {
+ public:
+ // "target" is the vertex whose containment will be determined.
+ explicit S2ContainsVertexQuery(const S2Point& target);
+
+ // Indicates that the polygon has an edge between "target" and "v" in the
+ // given direction (+1 = outgoing, -1 = incoming, 0 = degenerate).
+ void AddEdge(const S2Point& v, int direction);
+
+ // Returns +1 if the vertex is contained, -1 if it is not contained, and 0
+ // if the incident edges consisted of matched sibling pairs.
+ int ContainsSign();
+
+ private:
+ S2Point target_;
+ gtl::btree_map<S2Point, int> edge_map_;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline S2ContainsVertexQuery::S2ContainsVertexQuery(const S2Point& target)
+ : target_(target) {
+}
+
+inline void S2ContainsVertexQuery::AddEdge(const S2Point& v, int direction) {
+ edge_map_[v] += direction;
+}
+
+#endif // S2_S2CONTAINS_VERTEX_QUERY_H_
--- /dev/null
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2CONVEX_HULL_QUERY_H_
+#define S2_S2CONVEX_HULL_QUERY_H_
+
+#include <memory>
+#include <vector>
+
+#include "s2/_fp_contract_off.h"
+#include "s2/s2cap.h"
+#include "s2/s2latlng_rect.h"
+#include "s2/s2loop.h"
+#include "s2/s2polygon.h"
+#include "s2/s2polyline.h"
+
+// S2ConvexHullQuery builds the convex hull of any collection of points,
+// polylines, loops, and polygons. It returns a single convex loop.
+//
+// The convex hull is defined as the smallest convex region on the sphere that
+// contains all of your input geometry. Recall that a region is "convex" if
+// for every pair of points inside the region, the straight edge between them
+// is also inside the region. In our case, a "straight" edge is a geodesic,
+// i.e. the shortest path on the sphere between two points.
+//
+// Containment of input geometry is defined as follows:
+//
+// - Each input loop and polygon is contained by the convex hull exactly
+// (i.e., according to S2Polygon::Contains(S2Polygon)).
+//
+// - Each input point is either contained by the convex hull or is a vertex
+// of the convex hull. (Recall that S2Loops do not necessarily contain their
+// vertices.)
+//
+// - For each input polyline, the convex hull contains all of its vertices
+// according to the rule for points above. (The definition of convexity
+// then ensures that the convex hull also contains the polyline edges.)
+//
+// To use this class, call the Add*() methods to add your input geometry, and
+// then call GetConvexHull(). Note that GetConvexHull() does *not* reset the
+// state; you can continue adding geometry if desired and compute the convex
+// hull again. If you want to start from scratch, simply declare a new
+// S2ConvexHullQuery object (they are cheap to create).
+//
+// This class is not thread-safe. There are no "const" methods.
+class S2ConvexHullQuery {
+ public:
+ S2ConvexHullQuery();
+
+ // Add a point to the input geometry.
+ void AddPoint(const S2Point& point);
+
+ // Add a polyline to the input geometry.
+ void AddPolyline(const S2Polyline& polyline);
+
+ // Add a loop to the input geometry.
+ void AddLoop(const S2Loop& loop);
+
+ // Add a polygon to the input geometry.
+ void AddPolygon(const S2Polygon& polygon);
+
+ // Compute a bounding cap for the input geometry provided.
+ //
+ // Note that this method does not clear the geometry; you can continue
+ // adding to it and call this method again if desired.
+ S2Cap GetCapBound();
+
+ // Compute the convex hull of the input geometry provided.
+ //
+ // If there is no geometry, this method returns an empty loop containing no
+ // points (see S2Loop::is_empty()).
+ //
+ // If the geometry spans more than half of the sphere, this method returns a
+ // full loop containing the entire sphere (see S2Loop::is_full()).
+ //
+ // If the geometry contains 1 or 2 points, or a single edge, this method
+ // returns a very small loop consisting of three vertices (which are a
+ // superset of the input vertices).
+ //
+ // Note that this method does not clear the geometry; you can continue
+ // adding to it and call this method again if desired.
+ std::unique_ptr<S2Loop> GetConvexHull();
+
+ private:
+ void GetMonotoneChain(std::vector<S2Point>* output);
+ std::unique_ptr<S2Loop> GetSinglePointLoop(const S2Point& p);
+ std::unique_ptr<S2Loop> GetSingleEdgeLoop(const S2Point& a, const S2Point& b);
+
+ S2LatLngRect bound_;
+ std::vector<S2Point> points_;
+
+ S2ConvexHullQuery(const S2ConvexHullQuery&) = delete;
+ void operator=(const S2ConvexHullQuery&) = delete;
+};
+
+#endif // S2_S2CONVEX_HULL_QUERY_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// This file contains documentation of the various coordinate systems used
+// throughout the library. Most importantly, S2 defines a framework for
+// decomposing the unit sphere into a hierarchy of "cells". Each cell is a
+// quadrilateral bounded by four geodesics. The top level of the hierarchy is
+// obtained by projecting the six faces of a cube onto the unit sphere, and
+// lower levels are obtained by subdividing each cell into four children
+// recursively. Cells are numbered such that sequentially increasing cells
+// follow a continuous space-filling curve over the entire sphere. The
+// transformation is designed to make the cells at each level fairly uniform
+// in size.
+//
+//
+////////////////////////// S2Cell Decomposition /////////////////////////
+//
+// The following methods define the cube-to-sphere projection used by
+// the S2Cell decomposition.
+//
+// In the process of converting a latitude-longitude pair to a 64-bit cell
+// id, the following coordinate systems are used:
+//
+// (id)
+// An S2CellId is a 64-bit encoding of a face and a Hilbert curve position
+// on that face. The Hilbert curve position implicitly encodes both the
+// position of a cell and its subdivision level (see s2cell_id.h).
+//
+// (face, i, j)
+// Leaf-cell coordinates. "i" and "j" are integers in the range
+// [0,(2**30)-1] that identify a particular leaf cell on the given face.
+// The (i, j) coordinate system is right-handed on each face, and the
+// faces are oriented such that Hilbert curves connect continuously from
+// one face to the next.
+//
+// (face, s, t)
+// Cell-space coordinates. "s" and "t" are real numbers in the range
+// [0,1] that identify a point on the given face. For example, the point
+// (s, t) = (0.5, 0.5) corresponds to the center of the top-level face
+// cell. This point is also a vertex of exactly four cells at each
+// subdivision level greater than zero.
+//
+// (face, si, ti)
+// Discrete cell-space coordinates. These are obtained by multiplying
+// "s" and "t" by 2**31 and rounding to the nearest unsigned integer.
+// Discrete coordinates lie in the range [0,2**31]. This coordinate
+// system can represent the edge and center positions of all cells with
+// no loss of precision (including non-leaf cells). In binary, each
+// coordinate of a level-k cell center ends with a 1 followed by
+// (30 - k) 0s. The coordinates of its edges end with (at least)
+// (31 - k) 0s.
+//
+// (face, u, v)
+// Cube-space coordinates in the range [-1,1]. To make the cells at each
+// level more uniform in size after they are projected onto the sphere,
+// we apply a nonlinear transformation of the form u=f(s), v=f(t).
+// The (u, v) coordinates after this transformation give the actual
+// coordinates on the cube face (modulo some 90 degree rotations) before
+// it is projected onto the unit sphere.
+//
+// (face, u, v, w)
+// Per-face coordinate frame. This is an extension of the (face, u, v)
+// cube-space coordinates that adds a third axis "w" in the direction of
+// the face normal. It is always a right-handed 3D coordinate system.
+// Cube-space coordinates can be converted to this frame by setting w=1,
+// while (u,v,w) coordinates can be projected onto the cube face by
+// dividing by w, i.e. (face, u/w, v/w).
+//
+// (x, y, z)
+// Direction vector (S2Point). Direction vectors are not necessarily unit
+// length, and are often chosen to be points on the biunit cube
+// [-1,+1]x[-1,+1]x[-1,+1]. They can be be normalized to obtain the
+// corresponding point on the unit sphere.
+//
+// (lat, lng)
+// Latitude and longitude (S2LatLng). Latitudes must be between -90 and
+// 90 degrees inclusive, and longitudes must be between -180 and 180
+// degrees inclusive.
+//
+// Note that the (i, j), (s, t), (si, ti), and (u, v) coordinate systems are
+// right-handed on all six faces.
+
+#ifndef S2_S2COORDS_H_
+#define S2_S2COORDS_H_
+
+#include <algorithm>
+#include <cmath>
+
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+#include "s2/r2.h"
+#include "s2/s2coords_internal.h"
+#include "s2/s2point.h"
+#include "s2/util/math/mathutil.h"
+
+// S2 is a namespace for constants and simple utility functions that are used
+// throughout the S2 library. The name "S2" is derived from the mathematical
+// symbol for the two-dimensional unit sphere (note that the "2" refers to the
+// dimension of the surface, not the space it is embedded in).
+namespace S2 {
+
+// This is the number of levels needed to specify a leaf cell. This
+// constant is defined here so that the S2::Metric class and the conversion
+// functions below can be implemented without including s2cell_id.h. Please
+// see s2cell_id.h for other useful constants and conversion functions.
+const int kMaxCellLevel = 30;
+
+// The maximum index of a valid leaf cell plus one. The range of valid leaf
+// cell indices is [0..kLimitIJ-1].
+const int kLimitIJ = 1 << kMaxCellLevel; // == S2CellId::kMaxSize
+
+// The maximum value of an si- or ti-coordinate. The range of valid (si,ti)
+// values is [0..kMaxSiTi].
+unsigned const int kMaxSiTi = 1U << (kMaxCellLevel + 1);
+
+// Convert an s- or t-value to the corresponding u- or v-value. This is
+// a non-linear transformation from [-1,1] to [-1,1] that attempts to
+// make the cell sizes more uniform.
+double STtoUV(double s);
+
+// The inverse of the STtoUV transformation. Note that it is not always
+// true that UVtoST(STtoUV(x)) == x due to numerical errors.
+double UVtoST(double u);
+
+// Convert the i- or j-index of a leaf cell to the minimum corresponding s-
+// or t-value contained by that cell. The argument must be in the range
+// [0..2**30], i.e. up to one position beyond the normal range of valid leaf
+// cell indices.
+double IJtoSTMin(int i);
+
+// Return the i- or j-index of the leaf cell containing the given
+// s- or t-value. If the argument is outside the range spanned by valid
+// leaf cell indices, return the index of the closest valid leaf cell (i.e.,
+// return values are clamped to the range of valid leaf cell indices).
+int STtoIJ(double s);
+
+// Convert an si- or ti-value to the corresponding s- or t-value.
+double SiTitoST(unsigned int si);
+
+// Return the si- or ti-coordinate that is nearest to the given s- or
+// t-value. The result may be outside the range of valid (si,ti)-values.
+unsigned int STtoSiTi(double s);
+
+// Convert (face, u, v) coordinates to a direction vector (not
+// necessarily unit length).
+S2Point FaceUVtoXYZ(int face, double u, double v);
+S2Point FaceUVtoXYZ(int face, const R2Point& uv);
+
+// If the dot product of p with the given face normal is positive,
+// set the corresponding u and v values (which may lie outside the range
+// [-1,1]) and return true. Otherwise return false.
+bool FaceXYZtoUV(int face, const S2Point& p,
+ double* pu, double* pv);
+bool FaceXYZtoUV(int face, const S2Point& p, R2Point* puv);
+
+// Given a *valid* face for the given point p (meaning that dot product
+// of p with the face normal is positive), return the corresponding
+// u and v values (which may lie outside the range [-1,1]).
+void ValidFaceXYZtoUV(int face, const S2Point& p,
+ double* pu, double* pv);
+void ValidFaceXYZtoUV(int face, const S2Point& p, R2Point* puv);
+
+// Transform the given point P to the (u,v,w) coordinate frame of the given
+// face (where the w-axis represents the face normal).
+S2Point FaceXYZtoUVW(int face, const S2Point& p);
+
+// Return the face containing the given direction vector. (For points on
+// the boundary between faces, the result is arbitrary but repeatable.)
+int GetFace(const S2Point& p);
+
+// Convert a direction vector (not necessarily unit length) to
+// (face, u, v) coordinates.
+int XYZtoFaceUV(const S2Point& p, double* pu, double* pv);
+int XYZtoFaceUV(const S2Point& p, R2Point* puv);
+
+// Convert a direction vector (not necessarily unit length) to
+// (face, si, ti) coordinates and, if p is exactly equal to the center of a
+// cell, return the level of this cell (-1 otherwise).
+int XYZtoFaceSiTi(const S2Point& p, int* face,
+ unsigned int* si, unsigned int* ti);
+
+// Convert (face, si, ti) coordinates to a direction vector (not necessarily
+// unit length).
+S2Point FaceSiTitoXYZ(int face, unsigned int si, unsigned int ti);
+
+// Return the right-handed normal (not necessarily unit length) for an
+// edge in the direction of the positive v-axis at the given u-value on
+// the given face. (This vector is perpendicular to the plane through
+// the sphere origin that contains the given edge.)
+S2Point GetUNorm(int face, double u);
+
+// Return the right-handed normal (not necessarily unit length) for an
+// edge in the direction of the positive u-axis at the given v-value on
+// the given face.
+S2Point GetVNorm(int face, double v);
+
+// Return the unit-length normal, u-axis, or v-axis for the given face.
+S2Point GetNorm(int face);
+S2Point GetUAxis(int face);
+S2Point GetVAxis(int face);
+
+// Return the given axis of the given face (u=0, v=1, w=2).
+S2Point GetUVWAxis(int face, int axis);
+
+// With respect to the (u,v,w) coordinate system of a given face, return the
+// face that lies in the given direction (negative=0, positive=1) of the
+// given axis (u=0, v=1, w=2). For example, GetUVWFace(4, 0, 1) returns the
+// face that is adjacent to face 4 in the positive u-axis direction.
+int GetUVWFace(int face, int axis, int direction);
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+// We have implemented three different projections from cell-space (s,t) to
+// cube-space (u,v): linear, quadratic, and tangent. They have the following
+// tradeoffs:
+//
+// Linear - This is the fastest transformation, but also produces the least
+// uniform cell sizes. Cell areas vary by a factor of about 5.2, with the
+// largest cells at the center of each face and the smallest cells in
+// the corners.
+//
+// Tangent - Transforming the coordinates via atan() makes the cell sizes
+// more uniform. The areas vary by a maximum ratio of 1.4 as opposed to a
+// maximum ratio of 5.2. However, each call to atan() is about as expensive
+// as all of the other calculations combined when converting from points to
+// cell ids, i.e. it reduces performance by a factor of 3.
+//
+// Quadratic - This is an approximation of the tangent projection that
+// is much faster and produces cells that are almost as uniform in size.
+// It is about 3 times faster than the tangent projection for converting
+// cell ids to points or vice versa. Cell areas vary by a maximum ratio of
+// about 2.1.
+//
+// Here is a table comparing the cell uniformity using each projection. "Area
+// ratio" is the maximum ratio over all subdivision levels of the largest cell
+// area to the smallest cell area at that level, "edge ratio" is the maximum
+// ratio of the longest edge of any cell to the shortest edge of any cell at
+// the same level, and "diag ratio" is the ratio of the longest diagonal of
+// any cell to the shortest diagonal of any cell at the same level. "ToPoint"
+// and "FromPoint" are the times in microseconds required to convert cell ids
+// to and from points (unit vectors) respectively. "ToPointRaw" is the time
+// to convert to a non-unit-length vector, which is all that is needed for
+// some purposes.
+//
+// Area Edge Diag ToPointRaw ToPoint FromPoint
+// Ratio Ratio Ratio (microseconds)
+// -------------------------------------------------------------------
+// Linear: 5.200 2.117 2.959 0.020 0.087 0.085
+// Tangent: 1.414 1.414 1.704 0.237 0.299 0.258
+// Quadratic: 2.082 1.802 1.932 0.033 0.096 0.108
+//
+// The worst-case cell aspect ratios are about the same with all three
+// projections. The maximum ratio of the longest edge to the shortest edge
+// within the same cell is about 1.4 and the maximum ratio of the diagonals
+// within the same cell is about 1.7.
+//
+// This data was produced using s2cell_test and s2cell_id_test.
+
+#define S2_LINEAR_PROJECTION 0
+#define S2_TAN_PROJECTION 1
+#define S2_QUADRATIC_PROJECTION 2
+
+#define S2_PROJECTION S2_QUADRATIC_PROJECTION
+
+#if S2_PROJECTION == S2_LINEAR_PROJECTION
+
+inline double STtoUV(double s) {
+ return 2 * s - 1;
+}
+
+inline double UVtoST(double u) {
+ return 0.5 * (u + 1);
+}
+
+#elif S2_PROJECTION == S2_TAN_PROJECTION
+
+inline double STtoUV(double s) {
+ // Unfortunately, tan(M_PI_4) is slightly less than 1.0. This isn't due to
+ // a flaw in the implementation of tan(), it's because the derivative of
+ // tan(x) at x=pi/4 is 2, and it happens that the two adjacent floating
+ // point numbers on either side of the infinite-precision value of pi/4 have
+ // tangents that are slightly below and slightly above 1.0 when rounded to
+ // the nearest double-precision result.
+
+ s = std::tan(M_PI_2 * s - M_PI_4);
+ return s + (1.0 / (int64{1} << 53)) * s;
+}
+
+inline double UVtoST(double u) {
+ volatile double a = std::atan(u);
+ return (2 * M_1_PI) * (a + M_PI_4);
+}
+
+#elif S2_PROJECTION == S2_QUADRATIC_PROJECTION
+
+inline double STtoUV(double s) {
+ if (s >= 0.5) return (1/3.) * (4*s*s - 1);
+ else return (1/3.) * (1 - 4*(1-s)*(1-s));
+}
+
+inline double UVtoST(double u) {
+ if (u >= 0) return 0.5 * std::sqrt(1 + 3*u);
+ else return 1 - 0.5 * std::sqrt(1 - 3*u);
+}
+
+#else
+
+#error Unknown value for S2_PROJECTION
+
+#endif
+
+inline double IJtoSTMin(int i) {
+ S2_DCHECK(i >= 0 && i <= kLimitIJ);
+ return (1.0 / kLimitIJ) * i;
+}
+
+inline int STtoIJ(double s) {
+ return std::max(0, std::min(kLimitIJ - 1,
+ MathUtil::FastIntRound(kLimitIJ * s - 0.5)));
+}
+
+inline double SiTitoST(unsigned int si) {
+ S2_DCHECK_LE(si, kMaxSiTi);
+ return (1.0 / kMaxSiTi) * si;
+}
+
+inline unsigned int STtoSiTi(double s) {
+ // kMaxSiTi == 2^31, so the result doesn't fit in an int32 when s == 1.
+ return static_cast<unsigned int>(MathUtil::FastInt64Round(s * kMaxSiTi));
+}
+
+inline S2Point FaceUVtoXYZ(int face, double u, double v) {
+ switch (face) {
+ case 0: return S2Point( 1, u, v);
+ case 1: return S2Point(-u, 1, v);
+ case 2: return S2Point(-u, -v, 1);
+ case 3: return S2Point(-1, -v, -u);
+ case 4: return S2Point( v, -1, -u);
+ default: return S2Point( v, u, -1);
+ }
+}
+
+inline S2Point FaceUVtoXYZ(int face, const R2Point& uv) {
+ return FaceUVtoXYZ(face, uv[0], uv[1]);
+}
+
+inline void ValidFaceXYZtoUV(int face, const S2Point& p,
+ double* pu, double* pv) {
+ S2_DCHECK_GT(p.DotProd(GetNorm(face)), 0);
+ switch (face) {
+ case 0: *pu = p[1] / p[0]; *pv = p[2] / p[0]; break;
+ case 1: *pu = -p[0] / p[1]; *pv = p[2] / p[1]; break;
+ case 2: *pu = -p[0] / p[2]; *pv = -p[1] / p[2]; break;
+ case 3: *pu = p[2] / p[0]; *pv = p[1] / p[0]; break;
+ case 4: *pu = p[2] / p[1]; *pv = -p[0] / p[1]; break;
+ default: *pu = -p[1] / p[2]; *pv = -p[0] / p[2]; break;
+ }
+}
+
+inline void ValidFaceXYZtoUV(int face, const S2Point& p, R2Point* puv) {
+ ValidFaceXYZtoUV(face, p, &(*puv)[0], &(*puv)[1]);
+}
+
+inline int GetFace(const S2Point& p) {
+ int face = p.LargestAbsComponent();
+ if (p[face] < 0) face += 3;
+ return face;
+}
+
+inline int XYZtoFaceUV(const S2Point& p, double* pu, double* pv) {
+ int face = GetFace(p);
+ ValidFaceXYZtoUV(face, p, pu, pv);
+ return face;
+}
+
+inline int XYZtoFaceUV(const S2Point& p, R2Point* puv) {
+ return XYZtoFaceUV(p, &(*puv)[0], &(*puv)[1]);
+}
+
+inline bool FaceXYZtoUV(int face, const S2Point& p,
+ double* pu, double* pv) {
+ if (face < 3) {
+ if (p[face] <= 0) return false;
+ } else {
+ if (p[face-3] >= 0) return false;
+ }
+ ValidFaceXYZtoUV(face, p, pu, pv);
+ return true;
+}
+
+inline bool FaceXYZtoUV(int face, const S2Point& p, R2Point* puv) {
+ return FaceXYZtoUV(face, p, &(*puv)[0], &(*puv)[1]);
+}
+
+inline S2Point GetUNorm(int face, double u) {
+ switch (face) {
+ case 0: return S2Point( u, -1, 0);
+ case 1: return S2Point( 1, u, 0);
+ case 2: return S2Point( 1, 0, u);
+ case 3: return S2Point(-u, 0, 1);
+ case 4: return S2Point( 0, -u, 1);
+ default: return S2Point( 0, -1, -u);
+ }
+}
+
+inline S2Point GetVNorm(int face, double v) {
+ switch (face) {
+ case 0: return S2Point(-v, 0, 1);
+ case 1: return S2Point( 0, -v, 1);
+ case 2: return S2Point( 0, -1, -v);
+ case 3: return S2Point( v, -1, 0);
+ case 4: return S2Point( 1, v, 0);
+ default: return S2Point( 1, 0, v);
+ }
+}
+
+inline S2Point GetNorm(int face) {
+ return GetUVWAxis(face, 2);
+}
+
+inline S2Point GetUAxis(int face) {
+ return GetUVWAxis(face, 0);
+}
+
+inline S2Point GetVAxis(int face) {
+ return GetUVWAxis(face, 1);
+}
+
+inline S2Point GetUVWAxis(int face, int axis) {
+ const double* p = internal::kFaceUVWAxes[face][axis];
+ return S2Point(p[0], p[1], p[2]);
+}
+
+inline int GetUVWFace(int face, int axis, int direction) {
+ S2_DCHECK(face >= 0 && face <= 5);
+ S2_DCHECK(axis >= 0 && axis <= 2);
+ S2_DCHECK(direction >= 0 && direction <= 1);
+ return internal::kFaceUVWFaces[face][axis][direction];
+}
+
+} // namespace S2
+
+#endif // S2_S2COORDS_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef S2_S2COORDS_INTERNAL_H_
+#define S2_S2COORDS_INTERNAL_H_
+// Author: ericv@google.com (Eric Veach)
+
+namespace S2 {
+namespace internal {
+
+// The canonical Hilbert traversal order looks like an inverted 'U':
+// the subcells are visited in the order (0,0), (0,1), (1,1), (1,0).
+// The following tables encode the traversal order for various
+// orientations of the Hilbert curve (axes swapped and/or directions
+// of the axes reversed).
+
+// Together these flags define a cell orientation. If 'kSwapMask'
+// is true, then canonical traversal order is flipped around the
+// diagonal (i.e. i and j are swapped with each other). If
+// 'kInvertMask' is true, then the traversal order is rotated by 180
+// degrees (i.e. the bits of i and j are inverted, or equivalently,
+// the axis directions are reversed).
+int constexpr kSwapMask = 0x01;
+int constexpr kInvertMask = 0x02;
+
+// kIJtoPos[orientation][ij] -> pos
+//
+// Given a cell orientation and the (i,j)-index of a subcell (0=(0,0),
+// 1=(0,1), 2=(1,0), 3=(1,1)), return the order in which this subcell is
+// visited by the Hilbert curve (a position in the range [0..3]).
+extern const int kIJtoPos[4][4];
+
+// kPosToIJ[orientation][pos] -> ij
+//
+// Return the (i,j) index of the subcell at the given position 'pos' in the
+// Hilbert curve traversal order with the given orientation. This is the
+// inverse of the previous table:
+//
+// kPosToIJ[r][kIJtoPos[r][ij]] == ij
+extern const int kPosToIJ[4][4];
+
+// kPosToOrientation[pos] -> orientation_modifier
+//
+// Return a modifier indicating how the orientation of the child subcell
+// with the given traversal position [0..3] is related to the orientation
+// of the parent cell. The modifier should be XOR-ed with the parent
+// orientation to obtain the curve orientation in the child.
+extern const int kPosToOrientation[4];
+
+// The U,V,W axes for each face.
+extern const double kFaceUVWAxes[6][3][3];
+
+// The precomputed neighbors of each face (see GetUVWFace).
+extern const int kFaceUVWFaces[6][3][2];
+
+} // namespace internal
+} // namespace S2
+
+#endif // S2_S2COORDS_INTERNAL_H_
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2CROSSING_EDGE_QUERY_H_
+#define S2_S2CROSSING_EDGE_QUERY_H_
+
+#include <type_traits>
+#include <vector>
+
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/third_party/absl/container/inlined_vector.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/r2.h"
+#include "s2/r2rect.h"
+#include "s2/s2padded_cell.h"
+#include "s2/s2shape_index.h"
+#include "s2/s2shapeutil_shape_edge.h"
+#include "s2/s2shapeutil_shape_edge_id.h"
+
+// A parameter that controls the reporting of edge intersections.
+//
+// - CrossingType::INTERIOR reports intersections that occur at a point
+// interior to both edges (i.e., not at a vertex).
+//
+// - CrossingType::ALL reports all intersections, even those where two edges
+// intersect only because they share a common vertex.
+namespace s2shapeutil {
+enum class CrossingType { INTERIOR, ALL };
+} // namespace s2shapeutil
+
+// S2CrossingEdgeQuery is used to find edges or shapes that are crossed by
+// an edge. Here is an example showing how to index a set of polylines,
+// and then find the polylines that are crossed by a given edge AB:
+//
+// void Test(const vector<S2Polyline*>& polylines,`
+// const S2Point& a0, const S2Point &a1) {
+// MutableS2ShapeIndex index;
+// for (S2Polyline* polyline : polylines) {
+// index.Add(absl::make_unique<S2Polyline::Shape>(polyline));
+// }
+// S2CrossingEdgeQuery query(&index);
+// for (const auto& edge : query.GetCrossingEdges(a, b, CrossingType::ALL)) {
+// S2_CHECK_GE(S2::CrossingSign(a0, a1, edge.v0(), edge.v1()), 0);
+// }
+// }
+//
+// Note that if you need to query many edges, it is more efficient to declare
+// a single S2CrossingEdgeQuery object and reuse it so that temporary storage
+// does not need to be reallocated each time.
+//
+// If you want to find *all* pairs of crossing edges, use
+// s2shapeutil::VisitCrossingEdgePairs() instead.
+class S2CrossingEdgeQuery {
+ public:
+ using CrossingType = s2shapeutil::CrossingType; // Defined above.
+
+ // Convenience constructor that calls Init().
+ explicit S2CrossingEdgeQuery(const S2ShapeIndex* index);
+
+ // Default constructor; requires Init() to be called.
+ S2CrossingEdgeQuery();
+ ~S2CrossingEdgeQuery();
+
+ S2CrossingEdgeQuery(const S2CrossingEdgeQuery&) = delete;
+ void operator=(const S2CrossingEdgeQuery&) = delete;
+
+ const S2ShapeIndex& index() const { return *index_; }
+
+ // REQUIRES: "index" is not modified after this method is called.
+ void Init(const S2ShapeIndex* index);
+
+ // Returns all edges that intersect the given query edge (a0,a1) and that
+ // have the given CrossingType (ALL or INTERIOR). Edges are sorted and
+ // unique.
+ std::vector<s2shapeutil::ShapeEdge> GetCrossingEdges(
+ const S2Point& a0, const S2Point& a1, CrossingType type);
+
+ // A specialized version of GetCrossingEdges() that only returns the edges
+ // that belong to a particular S2Shape.
+ std::vector<s2shapeutil::ShapeEdge> GetCrossingEdges(
+ const S2Point& a0, const S2Point& a1,
+ const S2Shape& shape, CrossingType type);
+
+ // These versions can be more efficient when they are called many times,
+ // since they do not require allocating a new vector on each call.
+ void GetCrossingEdges(const S2Point& a0, const S2Point& a1, CrossingType type,
+ std::vector<s2shapeutil::ShapeEdge>* edges);
+
+ void GetCrossingEdges(const S2Point& a0, const S2Point& a1,
+ const S2Shape& shape, CrossingType type,
+ std::vector<s2shapeutil::ShapeEdge>* edges);
+
+
+ /////////////////////////// Low-Level Methods ////////////////////////////
+ //
+ // Most clients will not need the following methods. They can be slightly
+ // more efficient but are harder to use, since they require the client to do
+ // all the actual crossing tests.
+
+ // Returns a superset of the edges that intersect a query edge (a0, a1).
+ // This method is useful for clients that want to test intersections in some
+ // other way, e.g. using S2::EdgeOrVertexCrossing().
+ std::vector<s2shapeutil::ShapeEdgeId> GetCandidates(const S2Point& a0,
+ const S2Point& a1);
+
+ // A specialized version of GetCandidates() that only returns the edges that
+ // belong to a particular S2Shape.
+ std::vector<s2shapeutil::ShapeEdgeId> GetCandidates(const S2Point& a0,
+ const S2Point& a1,
+ const S2Shape& shape);
+
+ // These versions can be more efficient when they are called many times,
+ // since they do not require allocating a new vector on each call.
+ void GetCandidates(const S2Point& a0, const S2Point& a1,
+ std::vector<s2shapeutil::ShapeEdgeId>* edges);
+
+ void GetCandidates(const S2Point& a0, const S2Point& a1, const S2Shape& shape,
+ std::vector<s2shapeutil::ShapeEdgeId>* edges);
+
+ // A function that is called with each candidate intersecting edge. The
+ // function may return false in order to request that the algorithm should
+ // be terminated, i.e. no further crossings are needed.
+ using ShapeEdgeIdVisitor =
+ std::function<bool (const s2shapeutil::ShapeEdgeId& id)>;
+
+ // Visits a superset of the edges that intersect the query edge (a0, a1),
+ // terminating early if the given ShapeEdgeIdVisitor returns false (in which
+ // case this function returns false as well).
+ //
+ // CAVEAT: Edges may be visited more than once.
+ bool VisitRawCandidates(const S2Point& a0, const S2Point& a1,
+ const ShapeEdgeIdVisitor& visitor);
+
+ bool VisitRawCandidates(const S2Point& a0, const S2Point& a1,
+ const S2Shape& shape,
+ const ShapeEdgeIdVisitor& visitor);
+
+ // A function that is called with each S2ShapeIndexCell that might contain
+ // edges intersecting the given query edge. The function may return false
+ // in order to request that the algorithm should be terminated, i.e. no
+ // further crossings are needed.
+ using CellVisitor = std::function<bool (const S2ShapeIndexCell& cell)>;
+
+ // Visits all S2ShapeIndexCells that might contain edges intersecting the
+ // given query edge (a0, a1), terminating early if the given CellVisitor
+ // returns false (in which case this function returns false as well).
+ //
+ // NOTE: Each candidate cell is visited exactly once.
+ bool VisitCells(const S2Point& a0, const S2Point& a1,
+ const CellVisitor& visitor);
+
+ // Visits all S2ShapeIndexCells within "root" that might contain edges
+ // intersecting the given query edge (a0, a1), terminating early if the
+ // given CellVisitor returns false (in which case this function returns
+ // false as well).
+ //
+ // NOTE: Each candidate cell is visited exactly once.
+ //
+ // REQUIRES: root.padding() == 0
+ // [This low-level method does not support padding; the argument is supplied
+ // as an S2PaddedCell in order to avoid constructing it repeatedly when
+ // this method is called using different query edges with the same root.]
+ bool VisitCells(const S2Point& a0, const S2Point& a1,
+ const S2PaddedCell& root, const CellVisitor& visitor);
+
+
+ // Given a query edge AB and a cell "root", returns all S2ShapeIndex cells
+ // within "root" that might contain edges intersecting AB.
+ //
+ // REQUIRES: root.padding() == 0 (see above)
+ void GetCells(const S2Point& a0, const S2Point& a1, const S2PaddedCell& root,
+ std::vector<const S2ShapeIndexCell*>* cells);
+
+ private:
+ // Internal methods are documented with their definitions.
+ bool VisitCells(const S2PaddedCell& pcell, const R2Rect& edge_bound);
+ bool ClipVAxis(const R2Rect& edge_bound, double center, int i,
+ const S2PaddedCell& pcell);
+ void SplitUBound(const R2Rect& edge_bound, double u,
+ R2Rect child_bounds[2]) const;
+ void SplitVBound(const R2Rect& edge_bound, double v,
+ R2Rect child_bounds[2]) const;
+ static void SplitBound(const R2Rect& edge_bound, int u_end, double u,
+ int v_end, double v, R2Rect child_bounds[2]);
+
+ const S2ShapeIndex* index_ = nullptr;
+
+ //////////// Temporary storage used while processing a query ///////////
+
+ R2Point a0_, a1_;
+ S2ShapeIndex::Iterator iter_;
+ const CellVisitor* visitor_;
+
+ // Avoids repeated allocation when methods are called many times.
+ std::vector<s2shapeutil::ShapeEdgeId> tmp_candidates_;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline S2CrossingEdgeQuery::S2CrossingEdgeQuery(const S2ShapeIndex* index) {
+ Init(index);
+}
+
+#endif // S2_S2CROSSING_EDGE_QUERY_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// The S2 library defines extra validity checks throughout the code that can
+// optionally be enabled or disabled. By default, these validity checks are
+// enabled in debug-mode builds (including fastbuild) and disabled in
+// optimized builds.
+//
+// There are two ways to change the default behavior:
+//
+// - The command line --s2debug flag, which changes the global default.
+//
+// - The S2Debug enum, which allows validity checks to be enabled or disabled
+// for specific objects (e.g., an S2Polygon).
+//
+// If you want to intentionally create invalid geometry (e.g., in a test), the
+// S2Debug enum is preferable. For example, to create an invalid S2Polygon,
+// you can do this:
+//
+// S2Polygon invalid;
+// invalid.set_s2debug_override(S2Debug::DISABLE);
+//
+// There is also a convenience constructor:
+//
+// vector<unique_ptr<S2Loop>> loops = ...;
+// S2Polygon invalid(loops, S2Debug::DISABLE);
+//
+// There are a few checks that cannot be disabled this way (e.g., internal
+// functions that require S2Points to be unit length). If you absolutely need
+// to disable these checks, you can set FLAGS_s2debug for the duration of a
+// specific test like this:
+//
+// TEST(MyClass, InvalidGeometry) {
+// FLAGS_s2debug = false; // Automatically restored between tests
+// ...
+// }
+
+#ifndef S2_S2DEBUG_H_
+#define S2_S2DEBUG_H_
+
+#include "s2/base/commandlineflags.h"
+#include "s2/base/integral_types.h"
+
+// Command line flag that enables extra validity checking throughout the S2
+// code. It is turned on by default in debug-mode builds.
+DECLARE_bool(s2debug);
+
+// Class that allows the --s2debug validity checks to be enabled or disabled
+// for specific objects (e.g., see S2Polygon).
+enum class S2Debug : uint8 {
+ ALLOW, // Validity checks are controlled by --s2debug
+ DISABLE // No validity checks even when --s2debug is true
+};
+
+#endif // S2_S2DEBUG_H_
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2DISTANCE_TARGET_H_
+#define S2_S2DISTANCE_TARGET_H_
+
+#include "s2/s2cap.h"
+#include "s2/s2cell.h"
+#include "s2/s2shape_index.h"
+
+// S2DistanceTarget represents a geometric object to which distances are
+// measured. For example, there are subtypes for measuring distances to a
+// point, an edge, or to an S2ShapeIndex (an arbitrary collection of
+// geometry). S2DistanceTarget objects are provided for the benefit of
+// classes that measure distances and/or find nearby geometry, such as
+// S2ClosestEdgeQuery, S2ClosestPointQuery, and S2ClosestCellQuery.
+//
+// Implementations do *not* need to be thread-safe. They may cache data or
+// allocate temporary data structures in order to improve performance. For
+// this reason, S2DistanceTarget objects are typically passed as pointers
+// rather than as const references.
+//
+// The Distance template argument is used to represent distances. Usually
+// this type is a thin wrapper around S1ChordAngle, but another distance type
+// may be substituted as long as it implements the API below. This can be
+// used to change the comparison function (e.g., to find the furthest edges
+// from the target), to get more accuracy, or to measure non-spheroidal
+// distances (e.g., using the WGS84 ellipsoid).
+//
+// The Distance concept is as follows:
+//
+// class Distance {
+// public:
+// // Default and copy constructors, assignment operator:
+// Distance();
+// Distance(const Distance&);
+// Distance& operator=(const Distance&);
+//
+// // Factory methods:
+// static Distance Zero(); // Returns a zero distance.
+// static Distance Infinity(); // Larger than any valid distance.
+// static Distance Negative(); // Smaller than any valid distance.
+//
+// // Comparison operators:
+// friend bool operator==(Distance x, Distance y);
+// friend bool operator<(Distance x, Distance y);
+//
+// // Delta represents the positive difference between two distances.
+// // It is used together with operator-() to implement Options::max_error().
+// // Typically Distance::Delta is simply S1ChordAngle.
+// class Delta {
+// public:
+// Delta();
+// Delta(const Delta&);
+// Delta& operator=(const Delta&);
+// friend bool operator==(Delta x, Delta y);
+// static Delta Zero();
+// };
+//
+// // Subtraction operator. Note that the second argument represents a
+// // delta between two distances. This distinction is important for
+// // classes that compute maximum distances (e.g., S2FurthestEdgeQuery).
+// friend Distance operator-(Distance x, Delta delta);
+//
+// // Method that returns an upper bound on the S1ChordAngle corresponding
+// // to this Distance (needed to implement Options::max_distance
+// // efficiently). For example, if Distance measures WGS84 ellipsoid
+// // distance then the corresponding angle needs to be 0.56% larger.
+// S1ChordAngle GetChordAngleBound() const;
+// };
+template <class Distance>
+class S2DistanceTarget {
+ public:
+ using Delta = typename Distance::Delta;
+
+ virtual ~S2DistanceTarget() {}
+
+ // Returns an S2Cap that bounds the set of points whose distance to the
+ // target is Distance::Zero().
+ virtual S2Cap GetCapBound() = 0;
+
+ // If the distance to the point "p" "min_dist", then updates "min_dist" and
+ // returns true. Otherwise returns false.
+ virtual bool UpdateMinDistance(const S2Point& p, Distance* min_dist) = 0;
+
+ // If the distance to the edge (v0, v1) is less than "min_dist", then
+ // updates "min_dist" and returns true. Otherwise returns false.
+ virtual bool UpdateMinDistance(const S2Point& v0, const S2Point& v1,
+ Distance* min_dist) = 0;
+
+ // If the distance to the given S2Cell (including its interior) is less
+ // than "min_dist", then updates "min_dist" and returns true. Otherwise
+ // returns false.
+ virtual bool UpdateMinDistance(const S2Cell& cell, Distance* min_dist) = 0;
+
+ // Finds all polygons in the given "query_index" that completely contain a
+ // connected component of the target geometry. (For example, if the
+ // target consists of 10 points, this method finds polygons that contain
+ // any of those 10 points.) For each such polygon, "visitor" is called
+ // with the S2Shape of the polygon along with a point of the target
+ // geometry that is contained by that polygon.
+ //
+ // Optionally, any polygon that intersects the target geometry may also be
+ // returned. In other words, this method returns all polygons that
+ // contain any connected component of the target, along with an arbitrary
+ // subset of the polygons that intersect the target.
+ //
+ // For example, suppose that "query_index" contains two abutting polygons
+ // A and B. If the target consists of two points "a" contained by A and
+ // "b" contained by B, then both A and B are returned. But if the target
+ // consists of the edge "ab", then any subset of {A, B} could be returned
+ // (because both polygons intersect the target but neither one contains
+ // the edge "ab").
+ //
+ // If "visitor" returns false, this method terminates early and returns
+ // false as well. Otherwise returns true.
+ //
+ // NOTE(ericv): This method exists only for the purpose of implementing
+ // S2ClosestEdgeQuery::Options::include_interiors() efficiently. Its API is
+ // unlikely to be useful for other purposes.
+ using ShapeVisitor = std::function<bool (S2Shape* containing_shape,
+ const S2Point& target_point)>;
+ virtual bool VisitContainingShapes(const S2ShapeIndex& query_index,
+ const ShapeVisitor& visitor) = 0;
+
+ // Specifies that whenever one of the UpdateMinDistance() methods above
+ // returns "true", the returned distance is allowed to be up to "max_error"
+ // larger than the true minimum distance. In other words, it gives this
+ // target object permission to terminate its distance calculation as soon as
+ // it has determined that (1) the minimum distance is less than "min_dist"
+ // and (2) the best possible further improvement is less than "max_error".
+ //
+ // If the target takes advantage of "max_error" to optimize its distance
+ // calculation, this method must return "true". (Most target types can use
+ // the default implementation which simply returns false.)
+ virtual bool set_max_error(const Delta& max_error) { return false; }
+
+ // The following method is provided as a convenience for classes that
+ // compute distances to a collection of indexed geometry, such as
+ // S2ClosestPointQuery, S2ClosestEdgeQuery, and S2ClosestCellQuery. It
+ // returns the maximum number of indexed objects for which it is faster to
+ // compute the distance by brute force (e.g., by testing every edge) rather
+ // than by using an index. (The appropriate value is different for each
+ // index type and can be estimated for a given (distance target, index type)
+ // pair by running benchmarks.)
+ //
+ // By default this method returns -1, indicating that it is not implemented.
+ virtual int max_brute_force_index_size() const { return -1; }
+};
+
+#endif // S2_S2DISTANCE_TARGET_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// The earth modeled as a sphere. There are lots of convenience
+// functions so that it doesn't take 2 lines of code just to do
+// a single conversion.
+
+#ifndef S2_S2EARTH_H_
+#define S2_S2EARTH_H_
+
+#include "s2/s1angle.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s2latlng.h"
+#include "s2/s2point.h"
+#include "s2/util/units/length-units.h"
+
+class S2Earth {
+ public:
+ // These functions convert between distances on the unit sphere
+ // (expressed as angles subtended from the sphere's center) and
+ // distances on the Earth's surface. This is possible only because
+ // the Earth is modeled as a sphere; otherwise a given angle would
+ // correspond to a range of distances depending on where the
+ // corresponding line segment was located.
+ //
+ // Note that you will lose precision if you use the ToDistance() method,
+ // since Meters is a single-precision type. If you need more precision,
+ // use one of the direct conversion methods below.
+ inline static S1Angle ToAngle(const util::units::Meters& distance);
+ inline static S1ChordAngle ToChordAngle(const util::units::Meters& distance);
+ inline static util::units::Meters ToDistance(const S1Angle& angle);
+ inline static util::units::Meters ToDistance(const S1ChordAngle& cangle);
+
+ // Convenience functions. These methods also return a double-precision
+ // result, unlike the generic ToDistance() method.
+ inline static double ToRadians(const util::units::Meters& distance);
+ inline static double ToMeters(const S1Angle& angle);
+ inline static double ToMeters(const S1ChordAngle& cangle);
+ inline static double ToKm(const S1Angle& angle);
+ inline static double ToKm(const S1ChordAngle& cangle);
+ inline static double KmToRadians(double km);
+ inline static double RadiansToKm(double radians);
+ inline static double MetersToRadians(double meters);
+ inline static double RadiansToMeters(double radians);
+
+ // These functions convert between areas on the unit sphere
+ // (as returned by the S2 library) and areas on the Earth's surface.
+ // Note that the area of a region on the unit sphere is equal to the
+ // solid angle it subtends from the sphere's center (measured in steradians).
+ inline static double SquareKmToSteradians(double km2);
+ inline static double SquareMetersToSteradians(double m2);
+ inline static double SteradiansToSquareKm(double steradians);
+ inline static double SteradiansToSquareMeters(double steradians);
+
+ // Convenience function for the frequent case where you need to call
+ // ToRadians in order to convert an east-west distance on the globe to
+ // radians. The output is a function of how close to the poles you are
+ // (i.e. at the bulge at the equator, one unit of longitude represents a
+ // much farther distance). The function will never return more than 2*PI
+ // radians, even if you're trying to go 100 million miles west at the north
+ // pole.
+ static double ToLongitudeRadians(const util::units::Meters& distance,
+ double latitude_radians);
+
+ // Computes the initial bearing from a to b. This is the bearing an observer
+ // at point a has when facing point b. A bearing of 0 degrees is north, and it
+ // increases clockwise (90 degrees is east, etc).
+ // If a == b, a == -b, or a is one of the Earths' poles, the return value is
+ // undefined.
+ static S1Angle GetInitialBearing(const S2LatLng& a, const S2LatLng& b);
+
+ // Returns the distance between two points. Example:
+ // double miles = Miles(geostore::S2Earth::GetDistance(a, b)).value();
+ //
+ // Note that these methods only have single-precision accuracy, since
+ // Meters is a single-precision type. If you ned more precision, use one
+ // of the methods below.
+ inline static util::units::Meters GetDistance(const S2Point& a,
+ const S2Point& b);
+ inline static util::units::Meters GetDistance(const S2LatLng& a,
+ const S2LatLng& b);
+
+ // Convenience functions. These methods also return a double-precision
+ // result, unlike the generic GetDistance() method.
+ inline static double GetDistanceKm(const S2Point& a, const S2Point& b);
+ inline static double GetDistanceKm(const S2LatLng& a, const S2LatLng& b);
+ inline static double GetDistanceMeters(const S2Point& a, const S2Point& b);
+ inline static double GetDistanceMeters(const S2LatLng& a, const S2LatLng& b);
+
+ // Returns the Earth's mean radius, which is the radius of the equivalent
+ // sphere with the same surface area. According to NASA, this value is
+ // 6371.01 +/- 0.02 km. The equatorial radius is 6378.136 km, and the polar
+ // radius is 6356.752 km. They differ by one part in 298.257.
+ //
+ // Reference: http://ssd.jpl.nasa.gov/phys_props_earth.html, which quotes
+ // Yoder, C.F. 1995. "Astrometric and Geodetic Properties of Earth and the
+ // Solar System" in Global Earth Physics, A Handbook of Physical Constants,
+ // AGU Reference Shelf 1, American Geophysical Union, Table 2.
+ inline static util::units::Meters Radius();
+
+ // Convenience functions.
+ inline static double RadiusKm();
+ inline static double RadiusMeters();
+
+ // Returns the altitude of the lowest known point on Earth. The lowest known
+ // point on Earth is the Challenger Deep with an altitude of -10898 meters
+ // above the surface of the spherical earth.
+ inline static util::units::Meters LowestAltitude();
+
+ // Convenience functions.
+ inline static double LowestAltitudeKm();
+ inline static double LowestAltitudeMeters();
+
+ // Returns the altitude of the highest known point on Earth. The highest
+ // known point on Earth is Mount Everest with an altitude of 8846 meters
+ // above the surface of the spherical earth.
+ inline static util::units::Meters HighestAltitude();
+
+ // Convenience functions.
+ inline static double HighestAltitudeKm();
+ inline static double HighestAltitudeMeters();
+};
+
+inline S1Angle S2Earth::ToAngle(const util::units::Meters& distance) {
+ return S1Angle::Radians(ToRadians(distance));
+}
+
+inline S1ChordAngle S2Earth::ToChordAngle(const util::units::Meters& distance) {
+ return S1ChordAngle(ToAngle(distance));
+}
+
+inline util::units::Meters S2Earth::ToDistance(const S1Angle& angle) {
+ return util::units::Meters(ToMeters(angle));
+}
+
+inline util::units::Meters S2Earth::ToDistance(const S1ChordAngle& cangle) {
+ return util::units::Meters(ToMeters(cangle));
+}
+
+inline double S2Earth::ToRadians(const util::units::Meters& distance) {
+ return distance.value() / RadiusMeters();
+}
+
+inline double S2Earth::ToMeters(const S1Angle& angle) {
+ return angle.radians() * RadiusMeters();
+}
+
+inline double S2Earth::ToKm(const S1Angle& angle) {
+ return angle.radians() * RadiusKm();
+}
+
+inline double S2Earth::ToMeters(const S1ChordAngle& cangle) {
+ return ToMeters(cangle.ToAngle());
+}
+
+inline double S2Earth::ToKm(const S1ChordAngle& cangle) {
+ return ToKm(cangle.ToAngle());
+}
+
+inline double S2Earth::KmToRadians(double km) {
+ return km / RadiusKm();
+}
+
+inline double S2Earth::RadiansToKm(double radians) {
+ return radians * RadiusKm();
+}
+
+inline double S2Earth::MetersToRadians(double meters) {
+ return meters / RadiusMeters();
+}
+
+inline double S2Earth::RadiansToMeters(double radians) {
+ return radians * RadiusMeters();
+}
+
+inline double S2Earth::SquareKmToSteradians(double km2) {
+ return km2 / (RadiusKm() * RadiusKm());
+}
+
+inline double S2Earth::SquareMetersToSteradians(double m2) {
+ return m2 / (RadiusMeters() * RadiusMeters());
+}
+
+inline double S2Earth::SteradiansToSquareKm(double steradians) {
+ return steradians * RadiusKm() * RadiusKm();
+}
+
+inline double S2Earth::SteradiansToSquareMeters(double steradians) {
+ return steradians * RadiusMeters() * RadiusMeters();
+}
+
+inline util::units::Meters S2Earth::GetDistance(const S2Point& a,
+ const S2Point& b) {
+ return ToDistance(S1Angle(a, b));
+}
+
+inline util::units::Meters S2Earth::GetDistance(const S2LatLng& a,
+ const S2LatLng& b) {
+ return ToDistance(a.GetDistance(b));
+}
+
+inline double S2Earth::GetDistanceKm(const S2Point& a, const S2Point& b) {
+ return RadiansToKm(a.Angle(b));
+}
+
+inline double S2Earth::GetDistanceKm(const S2LatLng& a, const S2LatLng& b) {
+ return ToKm(a.GetDistance(b));
+}
+
+inline double S2Earth::GetDistanceMeters(const S2Point& a, const S2Point& b) {
+ return RadiansToMeters(a.Angle(b));
+}
+
+inline double S2Earth::GetDistanceMeters(const S2LatLng& a, const S2LatLng& b) {
+ return ToMeters(a.GetDistance(b));
+}
+
+inline util::units::Meters S2Earth::Radius() {
+ return util::units::Meters(RadiusMeters());
+}
+
+inline double S2Earth::RadiusKm() {
+ return 0.001 * RadiusMeters();
+}
+
+inline double S2Earth::RadiusMeters() {
+ return 6371010.0;
+}
+
+inline util::units::Meters S2Earth::LowestAltitude() {
+ return util::units::Meters(LowestAltitudeMeters());
+}
+
+inline double S2Earth::LowestAltitudeKm() {
+ return 0.001 * LowestAltitudeMeters();
+}
+
+inline double S2Earth::LowestAltitudeMeters() {
+ return -10898;
+}
+
+inline util::units::Meters S2Earth::HighestAltitude() {
+ return util::units::Meters(HighestAltitudeMeters());
+}
+
+inline double S2Earth::HighestAltitudeKm() {
+ return 0.001 * HighestAltitudeMeters();
+}
+
+inline double S2Earth::HighestAltitudeMeters() {
+ return 8846;
+}
+
+#endif // S2_S2EARTH_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// Defines a collection of functions for:
+//
+// (1) Robustly clipping geodesic edges to the faces of the S2 biunit cube
+// (see s2coords.h), and
+//
+// (2) Robustly clipping 2D edges against 2D rectangles.
+//
+// These functions can be used to efficiently find the set of S2CellIds that
+// are intersected by a geodesic edge (e.g., see S2CrossingEdgeQuery).
+
+#ifndef S2_S2EDGE_CLIPPING_H_
+#define S2_S2EDGE_CLIPPING_H_
+
+#include <cmath>
+
+#include "s2/base/logging.h"
+#include "s2/third_party/absl/container/inlined_vector.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/r2.h"
+#include "s2/r2rect.h"
+#include "s2/s2point.h"
+
+namespace S2 {
+
+// FaceSegment represents an edge AB clipped to an S2 cube face. It is
+// represented by a face index and a pair of (u,v) coordinates.
+struct FaceSegment {
+ int face;
+ R2Point a, b;
+};
+using FaceSegmentVector = absl::InlinedVector<FaceSegment, 6>;
+
+// Subdivides the given edge AB at every point where it crosses the boundary
+// between two S2 cube faces and returns the corresponding FaceSegments. The
+// segments are returned in order from A toward B. The input points must be
+// unit length.
+//
+// This method guarantees that the returned segments form a continuous path
+// from A to B, and that all vertices are within kFaceClipErrorUVDist of the
+// line AB. All vertices lie within the [-1,1]x[-1,1] cube face rectangles.
+// The results are consistent with s2pred::Sign(), i.e. the edge is
+// well-defined even its endpoints are antipodal. [TODO(ericv): Extend the
+// implementation of S2::RobustCrossProd so that this statement is true.]
+void GetFaceSegments(const S2Point& a, const S2Point& b,
+ FaceSegmentVector* segments);
+
+// Given an edge AB and a face, returns the (u,v) coordinates for the portion
+// of AB that intersects that face. This method guarantees that the clipped
+// vertices lie within the [-1,1]x[-1,1] cube face rectangle and are within
+// kFaceClipErrorUVDist of the line AB, but the results may differ from
+// those produced by GetFaceSegments. Returns false if AB does not
+// intersect the given face.
+bool ClipToFace(const S2Point& a, const S2Point& b, int face,
+ R2Point* a_uv, R2Point* b_uv);
+
+// Like ClipToFace, but rather than clipping to the square [-1,1]x[-1,1]
+// in (u,v) space, this method clips to [-R,R]x[-R,R] where R=(1+padding).
+bool ClipToPaddedFace(const S2Point& a, const S2Point& b, int face,
+ double padding, R2Point* a_uv, R2Point* b_uv);
+
+// The maximum error in the vertices returned by GetFaceSegments and
+// ClipToFace (compared to an exact calculation):
+//
+// - kFaceClipErrorRadians is the maximum angle between a returned vertex
+// and the nearest point on the exact edge AB. It is equal to the
+// maximum directional error in S2::RobustCrossProd, plus the error when
+// projecting points onto a cube face.
+//
+// - kFaceClipErrorDist is the same angle expressed as a maximum distance
+// in (u,v)-space. In other words, a returned vertex is at most this far
+// from the exact edge AB projected into (u,v)-space.
+
+// - kFaceClipErrorUVCoord is the same angle expressed as the maximum error
+// in an individual u- or v-coordinate. In other words, for each
+// returned vertex there is a point on the exact edge AB whose u- and
+// v-coordinates differ from the vertex by at most this amount.
+
+extern const double kFaceClipErrorRadians;
+extern const double kFaceClipErrorUVDist;
+extern const double kFaceClipErrorUVCoord;
+
+// Returns true if the edge AB intersects the given (closed) rectangle to
+// within the error bound below.
+bool IntersectsRect(const R2Point& a, const R2Point& b, const R2Rect& rect);
+
+// The maximum error in IntersectRect. If some point of AB is inside the
+// rectangle by at least this distance, the result is guaranteed to be true;
+// if all points of AB are outside the rectangle by at least this distance,
+// the result is guaranteed to be false. This bound assumes that "rect" is
+// a subset of the rectangle [-1,1]x[-1,1] or extends slightly outside it
+// (e.g., by 1e-10 or less).
+extern const double kIntersectsRectErrorUVDist;
+
+// Given an edge AB, returns the portion of AB that is contained by the given
+// rectangle "clip". Returns false if there is no intersection.
+bool ClipEdge(const R2Point& a, const R2Point& b, const R2Rect& clip,
+ R2Point* a_clipped, R2Point* b_clipped);
+
+// Given an edge AB and a rectangle "clip", returns the bounding rectangle of
+// the portion of AB intersected by "clip". The resulting bound may be
+// empty. This is a convenience function built on top of ClipEdgeBound.
+R2Rect GetClippedEdgeBound(const R2Point& a, const R2Point& b,
+ const R2Rect& clip);
+
+// This function can be used to clip an edge AB to sequence of rectangles
+// efficiently. It represents the clipped edges by their bounding boxes
+// rather than as a pair of endpoints. Specifically, let A'B' be some
+// portion of an edge AB, and let "bound" be a tight bound of A'B'. This
+// function updates "bound" (in place) to be a tight bound of A'B'
+// intersected with a given rectangle "clip". If A'B' does not intersect
+// "clip", returns false and does not necessarily update "bound".
+//
+// REQUIRES: "bound" is a tight bounding rectangle for some portion of AB.
+// (This condition is automatically satisfied if you start with the bounding
+// box of AB and clip to a sequence of rectangles, stopping when the method
+// returns false.)
+bool ClipEdgeBound(const R2Point& a, const R2Point& b,
+ const R2Rect& clip, R2Rect* bound);
+
+// The maximum error in the vertices generated by ClipEdge and the bounds
+// generated by ClipEdgeBound (compared to an exact calculation):
+//
+// - kEdgeClipErrorUVCoord is the maximum error in a u- or v-coordinate
+// compared to the exact result, assuming that the points A and B are in
+// the rectangle [-1,1]x[1,1] or slightly outside it (by 1e-10 or less).
+//
+// - kEdgeClipErrorUVDist is the maximum distance from a clipped point to
+// the corresponding exact result. It is equal to the error in a single
+// coordinate because at most one coordinate is subject to error.
+
+extern const double kEdgeClipErrorUVCoord;
+extern const double kEdgeClipErrorUVDist;
+
+// Given a value x that is some linear combination of a and b, returns the
+// value x1 that is the same linear combination of a1 and b1. This function
+// makes the following guarantees:
+// - If x == a, then x1 = a1 (exactly).
+// - If x == b, then x1 = b1 (exactly).
+// - If a <= x <= b, then a1 <= x1 <= b1 (even if a1 == b1).
+// REQUIRES: a != b
+double InterpolateDouble(double x, double a, double b, double a1, double b1);
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline bool ClipToFace(const S2Point& a, const S2Point& b, int face,
+ R2Point* a_uv, R2Point* b_uv) {
+ return ClipToPaddedFace(a, b, face, 0.0, a_uv, b_uv);
+}
+
+inline double InterpolateDouble(double x, double a, double b,
+ double a1, double b1) {
+ S2_DCHECK_NE(a, b);
+ // To get results that are accurate near both A and B, we interpolate
+ // starting from the closer of the two points.
+ if (std::fabs(a - x) <= std::fabs(b - x)) {
+ return a1 + (b1 - a1) * (x - a) / (b - a);
+ } else {
+ return b1 + (a1 - b1) * (x - b) / (a - b);
+ }
+}
+
+} // namespace S2
+
+#endif // S2_S2EDGE_CLIPPING_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2EDGE_CROSSER_H_
+#define S2_S2EDGE_CROSSER_H_
+
+#include "s2/base/logging.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/s2edge_crossings.h"
+#include "s2/s2pointutil.h"
+#include "s2/s2predicates.h"
+
+class S2CopyingEdgeCrosser; // Forward declaration
+
+// This class allows edges to be efficiently tested for intersection with a
+// given fixed edge AB. It is especially efficient when testing for
+// intersection with an edge chain connecting vertices v0, v1, v2, ...
+//
+// Example usage:
+//
+// void CountIntersections(const S2Point& a, const S2Point& b,
+// const vector<pair<S2Point, S2Point>>& edges) {
+// int count = 0;
+// S2EdgeCrosser crosser(&a, &b);
+// for (const auto& edge : edges) {
+// if (crosser.CrossingSign(&edge.first, &edge.second) >= 0) {
+// ++count;
+// }
+// }
+// return count;
+// }
+//
+// This class expects that the client already has all the necessary vertices
+// stored in memory, so that this class can refer to them with pointers and
+// does not need to make its own copies. If this is not the case (e.g., you
+// want to pass temporary objects as vertices), see S2CopyingEdgeCrosser.
+class S2EdgeCrosser {
+ public:
+ // Default constructor; must be followed by a call to Init().
+ S2EdgeCrosser() {}
+
+ // Convenience constructor that calls Init() with the given fixed edge AB.
+ // The arguments "a" and "b" must point to values that persist for the
+ // lifetime of the S2EdgeCrosser object (or until the next Init() call).
+ S2EdgeCrosser(const S2Point* a, const S2Point* b);
+
+ // Accessors for the constructor arguments.
+ const S2Point* a() { return a_; }
+ const S2Point* b() { return b_; }
+
+ // Initialize the S2EdgeCrosser with the given fixed edge AB. The arguments
+ // "a" and "b" must point to values that persist for the lifetime of the
+ // S2EdgeCrosser object (or until the next Init() call).
+ void Init(const S2Point* a, const S2Point* b);
+
+ // This function determines whether the edge AB intersects the edge CD.
+ // Returns +1 if AB crosses CD at a point that is interior to both edges.
+ // Returns 0 if any two vertices from different edges are the same.
+ // Returns -1 otherwise.
+ //
+ // Note that if an edge is degenerate (A == B or C == D), the return value
+ // is 0 if two vertices from different edges are the same and -1 otherwise.
+ //
+ // Properties of CrossingSign:
+ //
+ // (1) CrossingSign(b,a,c,d) == CrossingSign(a,b,c,d)
+ // (2) CrossingSign(c,d,a,b) == CrossingSign(a,b,c,d)
+ // (3) CrossingSign(a,b,c,d) == 0 if a==c, a==d, b==c, b==d
+ // (3) CrossingSign(a,b,c,d) <= 0 if a==b or c==d (see above)
+ //
+ // This function implements an exact, consistent perturbation model such
+ // that no three points are ever considered to be collinear. This means
+ // that even if you have 4 points A, B, C, D that lie exactly in a line
+ // (say, around the equator), C and D will be treated as being slightly to
+ // one side or the other of AB. This is done in a way such that the
+ // results are always consistent (see s2pred::Sign).
+ //
+ // Note that if you want to check an edge against a chain of other edges,
+ // it is slightly more efficient to use the single-argument version of
+ // CrossingSign below.
+ //
+ // The arguments must point to values that persist until the next call.
+ int CrossingSign(const S2Point* c, const S2Point* d);
+
+ // This method extends the concept of a "crossing" to the case where AB
+ // and CD have a vertex in common. The two edges may or may not cross,
+ // according to the rules defined in VertexCrossing() below. The rules
+ // are designed so that point containment tests can be implemented simply
+ // by counting edge crossings. Similarly, determining whether one edge
+ // chain crosses another edge chain can be implemented by counting.
+ //
+ // Returns true if CrossingSign(c, d) > 0, or AB and CD share a vertex
+ // and VertexCrossing(a, b, c, d) returns true.
+ //
+ // The arguments must point to values that persist until the next call.
+ bool EdgeOrVertexCrossing(const S2Point* c, const S2Point* d);
+
+ ///////////////////////// Edge Chain Methods ///////////////////////////
+ //
+ // You don't need to use these unless you're trying to squeeze out every
+ // last drop of performance. Essentially all you are saving is a test
+ // whether the first vertex of the current edge is the same as the second
+ // vertex of the previous edge. Example usage:
+ //
+ // vector<S2Point> chain;
+ // crosser.RestartAt(&chain[0]);
+ // for (int i = 1; i < chain.size(); ++i) {
+ // if (crosser.EdgeOrVertexCrossing(&chain[i])) { ++count; }
+ // }
+
+ // Convenience constructor that uses AB as the fixed edge, and C as the
+ // first vertex of the vertex chain (equivalent to calling RestartAt(c)).
+ //
+ // The arguments must point to values that persist until the next call.
+ S2EdgeCrosser(const S2Point* a, const S2Point* b, const S2Point* c);
+
+ // Call this method when your chain 'jumps' to a new place.
+ // The argument must point to a value that persists until the next call.
+ void RestartAt(const S2Point* c);
+
+ // Like CrossingSign above, but uses the last vertex passed to one of
+ // the crossing methods (or RestartAt) as the first vertex of the current
+ // edge.
+ //
+ // The argument must point to a value that persists until the next call.
+ int CrossingSign(const S2Point* d);
+
+ // Like EdgeOrVertexCrossing above, but uses the last vertex passed to one
+ // of the crossing methods (or RestartAt) as the first vertex of the
+ // current edge.
+ //
+ // The argument must point to a value that persists until the next call.
+ bool EdgeOrVertexCrossing(const S2Point* d);
+
+ // Returns the last vertex of the current edge chain being tested, i.e. the
+ // C vertex that will be used to construct the edge CD when one of the
+ // methods above is called.
+ const S2Point* c() { return c_; }
+
+ private:
+ friend class S2CopyingEdgeCrosser;
+
+ // These functions handle the "slow path" of CrossingSign().
+ int CrossingSignInternal(const S2Point* d);
+ int CrossingSignInternal2(const S2Point& d);
+
+ // Used internally by S2CopyingEdgeCrosser. Updates "c_" only.
+ void set_c(const S2Point* c) { c_ = c; }
+
+ // The fields below are constant after the call to Init().
+ const S2Point* a_;
+ const S2Point* b_;
+ Vector3_d a_cross_b_;
+
+ // To reduce the number of calls to s2pred::ExpensiveSign(), we compute an
+ // outward-facing tangent at A and B if necessary. If the plane
+ // perpendicular to one of these tangents separates AB from CD (i.e., one
+ // edge on each side) then there is no intersection.
+ bool have_tangents_; // True if the tangents have been computed.
+ S2Point a_tangent_; // Outward-facing tangent at A.
+ S2Point b_tangent_; // Outward-facing tangent at B.
+
+ // The fields below are updated for each vertex in the chain.
+ const S2Point* c_; // Previous vertex in the vertex chain.
+ int acb_; // The orientation of triangle ACB.
+
+ // The field below is a temporary used by CrossingSignInternal().
+ int bda_; // The orientation of triangle BDA.
+
+ S2EdgeCrosser(const S2EdgeCrosser&) = delete;
+ void operator=(const S2EdgeCrosser&) = delete;
+};
+
+// S2CopyingEdgeCrosser is exactly like S2EdgeCrosser, except that it makes its
+// own copy of all arguments so that they do not need to persist between
+// calls. This is less efficient, but makes it possible to use points that
+// are generated on demand and cannot conveniently be stored by the client.
+class S2CopyingEdgeCrosser {
+ public:
+ // These methods are all exactly like S2EdgeCrosser, except that the
+ // arguments can be temporaries.
+ S2CopyingEdgeCrosser() {}
+ S2CopyingEdgeCrosser(const S2Point& a, const S2Point& b);
+ const S2Point& a() { return a_; }
+ const S2Point& b() { return b_; }
+ const S2Point& c() { return c_; }
+ void Init(const S2Point& a, const S2Point& b);
+ int CrossingSign(const S2Point& c, const S2Point& d);
+ bool EdgeOrVertexCrossing(const S2Point& c, const S2Point& d);
+ S2CopyingEdgeCrosser(const S2Point& a, const S2Point& b, const S2Point& c);
+ void RestartAt(const S2Point& c);
+ int CrossingSign(const S2Point& d);
+ bool EdgeOrVertexCrossing(const S2Point& d);
+
+ private:
+ S2Point a_, b_, c_;
+ // TODO(ericv): It would be more efficient to implement S2CopyingEdgeCrosser
+ // directly rather than as a wrapper around S2EdgeCrosser.
+ S2EdgeCrosser crosser_;
+
+ S2CopyingEdgeCrosser(const S2CopyingEdgeCrosser&) = delete;
+ void operator=(const S2CopyingEdgeCrosser&) = delete;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline S2EdgeCrosser::S2EdgeCrosser(const S2Point* a, const S2Point* b)
+ : a_(a), b_(b), a_cross_b_(a_->CrossProd(*b_)), have_tangents_(false),
+ c_(nullptr) {
+ S2_DCHECK(S2::IsUnitLength(*a));
+ S2_DCHECK(S2::IsUnitLength(*b));
+}
+
+inline void S2EdgeCrosser::Init(const S2Point* a, const S2Point* b) {
+ a_ = a;
+ b_ = b;
+ a_cross_b_ = a->CrossProd(*b_);
+ have_tangents_ = false;
+ c_ = nullptr;
+}
+
+inline int S2EdgeCrosser::CrossingSign(const S2Point* c, const S2Point* d) {
+ if (c != c_) RestartAt(c);
+ return CrossingSign(d);
+}
+
+inline bool S2EdgeCrosser::EdgeOrVertexCrossing(const S2Point* c,
+ const S2Point* d) {
+ if (c != c_) RestartAt(c);
+ return EdgeOrVertexCrossing(d);
+}
+
+inline S2EdgeCrosser::S2EdgeCrosser(
+ const S2Point* a, const S2Point* b, const S2Point* c)
+ : a_(a), b_(b), a_cross_b_(a_->CrossProd(*b_)), have_tangents_(false) {
+ S2_DCHECK(S2::IsUnitLength(*a));
+ S2_DCHECK(S2::IsUnitLength(*b));
+ RestartAt(c);
+}
+
+inline void S2EdgeCrosser::RestartAt(const S2Point* c) {
+ S2_DCHECK(S2::IsUnitLength(*c));
+ c_ = c;
+ acb_ = -s2pred::TriageSign(*a_, *b_, *c_, a_cross_b_);
+}
+
+inline int S2EdgeCrosser::CrossingSign(const S2Point* d) {
+ S2_DCHECK(S2::IsUnitLength(*d));
+ // For there to be an edge crossing, the triangles ACB, CBD, BDA, DAC must
+ // all be oriented the same way (CW or CCW). We keep the orientation of ACB
+ // as part of our state. When each new point D arrives, we compute the
+ // orientation of BDA and check whether it matches ACB. This checks whether
+ // the points C and D are on opposite sides of the great circle through AB.
+
+ // Recall that TriageSign is invariant with respect to rotating its
+ // arguments, i.e. ABD has the same orientation as BDA.
+ int bda = s2pred::TriageSign(*a_, *b_, *d, a_cross_b_);
+ if (acb_ == -bda && bda != 0) {
+ // The most common case -- triangles have opposite orientations. Save the
+ // current vertex D as the next vertex C, and also save the orientation of
+ // the new triangle ACB (which is opposite to the current triangle BDA).
+ c_ = d;
+ acb_ = -bda;
+ return -1;
+ }
+ bda_ = bda;
+ return CrossingSignInternal(d);
+}
+
+inline bool S2EdgeCrosser::EdgeOrVertexCrossing(const S2Point* d) {
+ // We need to copy c_ since it is clobbered by CrossingSign().
+ const S2Point* c = c_;
+ int crossing = CrossingSign(d);
+ if (crossing < 0) return false;
+ if (crossing > 0) return true;
+ return S2::VertexCrossing(*a_, *b_, *c, *d);
+}
+
+inline S2CopyingEdgeCrosser::S2CopyingEdgeCrosser(const S2Point& a,
+ const S2Point& b)
+ : a_(a), b_(b), c_(S2Point()), crosser_(&a_, &b_) {
+}
+
+inline void S2CopyingEdgeCrosser::Init(const S2Point& a, const S2Point& b) {
+ a_ = a;
+ b_ = b;
+ c_ = S2Point();
+ crosser_.Init(&a_, &b_);
+}
+
+inline int S2CopyingEdgeCrosser::CrossingSign(const S2Point& c,
+ const S2Point& d) {
+ if (c != c_ || crosser_.c_ == nullptr) RestartAt(c);
+ return CrossingSign(d);
+}
+
+inline bool S2CopyingEdgeCrosser::EdgeOrVertexCrossing(
+ const S2Point& c, const S2Point& d) {
+ if (c != c_ || crosser_.c_ == nullptr) RestartAt(c);
+ return EdgeOrVertexCrossing(d);
+}
+
+inline S2CopyingEdgeCrosser::S2CopyingEdgeCrosser(
+ const S2Point& a, const S2Point& b, const S2Point& c)
+ : a_(a), b_(b), c_(c), crosser_(&a_, &b_, &c) {
+}
+
+inline void S2CopyingEdgeCrosser::RestartAt(const S2Point& c) {
+ c_ = c;
+ crosser_.RestartAt(&c_);
+}
+
+inline int S2CopyingEdgeCrosser::CrossingSign(const S2Point& d) {
+ int result = crosser_.CrossingSign(&d);
+ c_ = d;
+ crosser_.set_c(&c_);
+ return result;
+}
+
+inline bool S2CopyingEdgeCrosser::EdgeOrVertexCrossing(const S2Point& d) {
+ bool result = crosser_.EdgeOrVertexCrossing(&d);
+ c_ = d;
+ crosser_.set_c(&c_);
+ return result;
+}
+
+#endif // S2_S2EDGE_CROSSER_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// Defines functions related to determining whether two geodesic edges cross
+// and for computing intersection points.
+//
+// The predicates CrossingSign(), VertexCrossing(), and EdgeOrVertexCrossing()
+// are robust, meaning that they produce correct, consistent results even in
+// pathological cases. See s2predicates.h for additional robust predicates.
+//
+// See also S2EdgeCrosser (which efficiently tests an edge against a sequence
+// of other edges) and S2CrossingEdgeQuery (which uses an index to speed up
+// the process).
+
+#ifndef S2_S2EDGE_CROSSINGS_H_
+#define S2_S2EDGE_CROSSINGS_H_
+
+#include <cmath>
+
+#include "s2/base/logging.h"
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/third_party/absl/container/inlined_vector.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/r2.h"
+#include "s2/r2rect.h"
+#include "s2/s1angle.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s1interval.h"
+#include "s2/s2latlng.h"
+#include "s2/s2latlng_rect.h"
+#include "s2/s2pointutil.h"
+#include "s2/s2predicates.h"
+#include "s2/util/math/vector.h"
+
+namespace S2 {
+
+// This function determines whether the edge AB intersects the edge CD.
+// Returns +1 if AB crosses CD at a point that is interior to both edges.
+// Returns 0 if any two vertices from different edges are the same.
+// Returns -1 otherwise.
+//
+// Note that if an edge is degenerate (A == B or C == D), the return value
+// is 0 if two vertices from different edges are the same and -1 otherwise.
+//
+// Properties of CrossingSign:
+//
+// (1) CrossingSign(b,a,c,d) == CrossingSign(a,b,c,d)
+// (2) CrossingSign(c,d,a,b) == CrossingSign(a,b,c,d)
+// (3) CrossingSign(a,b,c,d) == 0 if a==c, a==d, b==c, b==d
+// (3) CrossingSign(a,b,c,d) <= 0 if a==b or c==d (see above)
+//
+// This function implements an exact, consistent perturbation model such
+// that no three points are ever considered to be collinear. This means
+// that even if you have 4 points A, B, C, D that lie exactly in a line
+// (say, around the equator), C and D will be treated as being slightly to
+// one side or the other of AB. This is done in a way such that the
+// results are always consistent (see s2pred::Sign).
+//
+// Note that if you want to check an edge against a collection of other edges,
+// it is much more efficient to use an S2EdgeCrosser (see s2edge_crosser.h).
+int CrossingSign(const S2Point& a, const S2Point& b,
+ const S2Point& c, const S2Point& d);
+
+// Given two edges AB and CD where at least two vertices are identical
+// (i.e. CrossingSign(a,b,c,d) == 0), this function defines whether the
+// two edges "cross" in a such a way that point-in-polygon containment tests
+// can be implemented by counting the number of edge crossings. The basic
+// rule is that a "crossing" occurs if AB is encountered after CD during a
+// CCW sweep around the shared vertex starting from a fixed reference point.
+//
+// Note that according to this rule, if AB crosses CD then in general CD
+// does not cross AB. However, this leads to the correct result when
+// counting polygon edge crossings. For example, suppose that A,B,C are
+// three consecutive vertices of a CCW polygon. If we now consider the edge
+// crossings of a segment BP as P sweeps around B, the crossing number
+// changes parity exactly when BP crosses BA or BC.
+//
+// Useful properties of VertexCrossing (VC):
+//
+// (1) VC(a,a,c,d) == VC(a,b,c,c) == false
+// (2) VC(a,b,a,b) == VC(a,b,b,a) == true
+// (3) VC(a,b,c,d) == VC(a,b,d,c) == VC(b,a,c,d) == VC(b,a,d,c)
+// (3) If exactly one of a,b equals one of c,d, then exactly one of
+// VC(a,b,c,d) and VC(c,d,a,b) is true
+//
+// It is an error to call this method with 4 distinct vertices.
+bool VertexCrossing(const S2Point& a, const S2Point& b,
+ const S2Point& c, const S2Point& d);
+
+// A convenience function that calls CrossingSign() to handle cases
+// where all four vertices are distinct, and VertexCrossing() to handle
+// cases where two or more vertices are the same. This defines a crossing
+// function such that point-in-polygon containment tests can be implemented
+// by simply counting edge crossings.
+bool EdgeOrVertexCrossing(const S2Point& a, const S2Point& b,
+ const S2Point& c, const S2Point& d);
+
+// Given two edges AB and CD such that CrossingSign(A, B, C, D) > 0, returns
+// their intersection point. Useful properties of GetIntersection (GI):
+//
+// (1) GI(b,a,c,d) == GI(a,b,d,c) == GI(a,b,c,d)
+// (2) GI(c,d,a,b) == GI(a,b,c,d)
+//
+// The returned intersection point X is guaranteed to be very close to the
+// true intersection point of AB and CD, even if the edges intersect at a
+// very small angle. See "kIntersectionError" below for details.
+S2Point GetIntersection(const S2Point& a, const S2Point& b,
+ const S2Point& c, const S2Point& d);
+
+// kIntersectionError is an upper bound on the distance from the intersection
+// point returned by GetIntersection() to the true intersection point.
+extern const S1Angle kIntersectionError;
+
+// This value can be used as the S2Builder snap_radius() to ensure that edges
+// that have been displaced by up to kIntersectionError are merged back
+// together again. For example this can happen when geometry is intersected
+// with a set of tiles and then unioned. It is equal to twice the
+// intersection error because input edges might have been displaced in
+// opposite directions.
+extern const S1Angle kIntersectionMergeRadius; // 2 * kIntersectionError
+
+} // namespace S2
+
+#endif // S2_S2EDGE_CROSSINGS_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// The following functions are not part of the public API. Currently they are
+// only used internally for testing purposes.
+
+#ifndef S2_S2EDGE_CROSSINGS_INTERNAL_H_
+#define S2_S2EDGE_CROSSINGS_INTERNAL_H_
+
+#include "s2/s1angle.h"
+#include "s2/s2point.h"
+
+namespace S2 {
+namespace internal {
+
+// Returns the intersection point of two edges computed using exact arithmetic
+// and rounded to the nearest representable S2Point.
+S2Point GetIntersectionExact(const S2Point& a0, const S2Point& a1,
+ const S2Point& b0, const S2Point& b1);
+
+// The maximum error in the method above.
+extern const S1Angle kIntersectionExactError;
+
+// The following field is used exclusively by S2EdgeUtilTesting in order to
+// measure how often each intersection method is used by GetIntersection().
+// If non-nullptr, then it points to an array of integers indexed by an
+// IntersectionMethod enum value. Each call to GetIntersection() increments
+// the array entry corresponding to the intersection method that was used.
+extern int* intersection_method_tally_;
+
+// The list of intersection methods implemented by GetIntersection().
+enum class IntersectionMethod {
+ SIMPLE,
+ SIMPLE_LD,
+ STABLE,
+ STABLE_LD,
+ EXACT,
+ NUM_METHODS
+};
+const char* GetIntersectionMethodName(IntersectionMethod method);
+
+} // namespace internal
+} // namespace S2
+
+#endif // S2_S2EDGE_CROSSINGS_INTERNAL_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// Defines a collection of functions for computing the distance to an edge,
+// interpolating along an edge, projecting points onto edges, etc.
+
+#ifndef S2_S2EDGE_DISTANCES_H_
+#define S2_S2EDGE_DISTANCES_H_
+
+#include <utility>
+
+#include "s2/s1angle.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s2point.h"
+
+namespace S2 {
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////// (point, edge) functions ///////////////
+
+// Returns the minimum distance from X to any point on the edge AB. All
+// arguments should be unit length. The result is very accurate for small
+// distances but may have some numerical error if the distance is large
+// (approximately Pi/2 or greater). The case A == B is handled correctly.
+//
+// If you want to compare a distance against a fixed threshold, e.g.
+// if (S2::GetDistance(x, a, b) < limit)
+// then it is significantly faster to use UpdateMinDistance() below.
+S1Angle GetDistance(const S2Point& x, const S2Point& a, const S2Point& b);
+
+// Returns true if the distance from X to the edge AB is less than "limit".
+// (Specify limit.Successor() for "less than or equal to".) This method is
+// significantly faster than GetDistance(). If you want to compare against a
+// fixed S1Angle, you should convert it to an S1ChordAngle once and save the
+// value, since this step is relatively expensive.
+//
+// See s2pred::CompareEdgeDistance() for an exact version of this predicate.
+bool IsDistanceLess(const S2Point& x, const S2Point& a, const S2Point& b,
+ S1ChordAngle limit);
+
+// If the distance from X to the edge AB is less than "min_dist", this
+// method updates "min_dist" and returns true. Otherwise it returns false.
+// The case A == B is handled correctly.
+//
+// Use this method when you want to compute many distances and keep track of
+// the minimum. It is significantly faster than using GetDistance(),
+// because (1) using S1ChordAngle is much faster than S1Angle, and (2) it
+// can save a lot of work by not actually computing the distance when it is
+// obviously larger than the current minimum.
+bool UpdateMinDistance(const S2Point& x, const S2Point& a, const S2Point& b,
+ S1ChordAngle* min_dist);
+
+// If the maximum distance from X to the edge AB is greater than "max_dist",
+// this method updates "max_dist" and returns true. Otherwise it returns false.
+// The case A == B is handled correctly.
+bool UpdateMaxDistance(const S2Point& x, const S2Point& a, const S2Point& b,
+ S1ChordAngle* max_dist);
+
+// Returns the maximum error in the result of UpdateMinDistance (and
+// associated functions such as UpdateMinInteriorDistance, IsDistanceLess,
+// etc), assuming that all input points are normalized to within the bounds
+// guaranteed by S2Point::Normalize(). The error can be added or subtracted
+// from an S1ChordAngle "x" using x.PlusError(error).
+//
+// Note that accuracy goes down as the distance approaches 0 degrees or 180
+// degrees (for different reasons). Near 0 degrees the error is acceptable
+// for all practical purposes (about 1.2e-15 radians ~= 8 nanometers). For
+// exactly antipodal points the maximum error is quite high (0.5 meters), but
+// this error drops rapidly as the points move away from antipodality
+// (approximately 1 millimeter for points that are 50 meters from antipodal,
+// and 1 micrometer for points that are 50km from antipodal).
+//
+// TODO(ericv): Currently the error bound does not hold for edges whose
+// endpoints are antipodal to within about 1e-15 radians (less than 1 micron).
+// This could be fixed by extending S2::RobustCrossProd to use higher
+// precision when necessary.
+double GetUpdateMinDistanceMaxError(S1ChordAngle dist);
+
+// Returns true if the minimum distance from X to the edge AB is attained at
+// an interior point of AB (i.e., not an endpoint), and that distance is less
+// than "limit". (Specify limit.Successor() for "less than or equal to".)
+bool IsInteriorDistanceLess(const S2Point& x,
+ const S2Point& a, const S2Point& b,
+ S1ChordAngle limit);
+
+// If the minimum distance from X to AB is attained at an interior point of AB
+// (i.e., not an endpoint), and that distance is less than "min_dist", then
+// this method updates "min_dist" and returns true. Otherwise returns false.
+bool UpdateMinInteriorDistance(const S2Point& x,
+ const S2Point& a, const S2Point& b,
+ S1ChordAngle* min_dist);
+
+// Returns the point along the edge AB that is closest to the point X.
+// The fractional distance of this point along the edge AB can be obtained
+// using GetDistanceFraction() above. Requires that all vectors have
+// unit length.
+S2Point Project(const S2Point& x, const S2Point& a, const S2Point& b);
+
+// A slightly more efficient version of Project() where the cross product of
+// the two endpoints has been precomputed. The cross product does not need to
+// be normalized, but should be computed using S2::RobustCrossProd() for the
+// most accurate results. Requires that x, a, and b have unit length.
+S2Point Project(const S2Point& x, const S2Point& a, const S2Point& b,
+ const Vector3_d& a_cross_b);
+
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////// (point along edge) functions ///////////////
+
+
+// Given a point X and an edge AB, returns the distance ratio AX / (AX + BX).
+// If X happens to be on the line segment AB, this is the fraction "t" such
+// that X == Interpolate(t, A, B). Requires that A and B are distinct.
+double GetDistanceFraction(const S2Point& x,
+ const S2Point& a, const S2Point& b);
+
+// Returns the point X along the line segment AB whose distance from A is the
+// given fraction "t" of the distance AB. Does NOT require that "t" be
+// between 0 and 1. Note that all distances are measured on the surface of
+// the sphere, so this is more complicated than just computing (1-t)*a + t*b
+// and normalizing the result.
+S2Point Interpolate(double t, const S2Point& a, const S2Point& b);
+
+// Like Interpolate(), except that the parameter "ax" represents the desired
+// distance from A to the result X rather than a fraction between 0 and 1.
+S2Point InterpolateAtDistance(S1Angle ax, const S2Point& a, const S2Point& b);
+
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////// (edge, edge) functions ///////////////
+
+
+// Like UpdateMinDistance(), but computes the minimum distance between the
+// given pair of edges. (If the two edges cross, the distance is zero.)
+// The cases a0 == a1 and b0 == b1 are handled correctly.
+bool UpdateEdgePairMinDistance(const S2Point& a0, const S2Point& a1,
+ const S2Point& b0, const S2Point& b1,
+ S1ChordAngle* min_dist);
+
+// As above, but for maximum distances. If one edge crosses the antipodal
+// reflection of the other, the distance is Pi.
+bool UpdateEdgePairMaxDistance(const S2Point& a0, const S2Point& a1,
+ const S2Point& b0, const S2Point& b1,
+ S1ChordAngle* max_dist);
+
+// Returns the pair of points (a, b) that achieves the minimum distance
+// between edges a0a1 and b0b1, where "a" is a point on a0a1 and "b" is a
+// point on b0b1. If the two edges intersect, "a" and "b" are both equal to
+// the intersection point. Handles a0 == a1 and b0 == b1 correctly.
+std::pair<S2Point, S2Point> GetEdgePairClosestPoints(
+ const S2Point& a0, const S2Point& a1,
+ const S2Point& b0, const S2Point& b1);
+
+// Returns true if every point on edge B=b0b1 is no further than "tolerance"
+// from some point on edge A=a0a1. Equivalently, returns true if the directed
+// Hausdorff distance from B to A is no more than "tolerance".
+// Requires that tolerance is less than 90 degrees.
+bool IsEdgeBNearEdgeA(const S2Point& a0, const S2Point& a1,
+ const S2Point& b0, const S2Point& b1,
+ S1Angle tolerance);
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline bool IsDistanceLess(const S2Point& x, const S2Point& a,
+ const S2Point& b, S1ChordAngle limit) {
+ return UpdateMinDistance(x, a, b, &limit);
+}
+
+inline bool IsInteriorDistanceLess(const S2Point& x, const S2Point& a,
+ const S2Point& b, S1ChordAngle limit) {
+ return UpdateMinInteriorDistance(x, a, b, &limit);
+}
+
+} // namespace S2
+
+#endif // S2_S2EDGE_DISTANCES_H_
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2EDGE_TESSELLATOR_H_
+#define S2_S2EDGE_TESSELLATOR_H_
+
+#include <vector>
+#include "s2/r2.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s2point.h"
+#include "s2/s2projections.h"
+
+// Given an edge in some 2D projection (e.g., Mercator), S2EdgeTessellator
+// converts the edge into a chain of spherical geodesic edges such that the
+// maximum distance between the original edge and the geodesic edge chain is
+// at most "tolerance". Similarly, it can convert a spherical geodesic edge
+// into a chain of edges in a given 2D projection such that the maximum
+// distance between the geodesic edge and the chain of projected edges is at
+// most "tolerance".
+class S2EdgeTessellator {
+ public:
+ // Constructs an S2EdgeTessellator using the given projection and error
+ // tolerance. The projection object must be valid for the entire lifetime
+ // of this object. (Projections are typically declared once and reused.)
+ //
+ // Method | Input | Output
+ // ------------------|------------------------|-----------------------
+ // AppendProjected | S2 geodesics | Planar projected edges
+ // AppendUnprojected | Planar projected edges | S2 geodesics
+ S2EdgeTessellator(const S2::Projection* projection, S1Angle tolerance);
+
+ // Converts the spherical geodesic edge AB to a chain of planar edges in the
+ // given projection and appends the corresponding vertices to "vertices".
+ //
+ // This method can be called multiple times with the same output vector to
+ // convert an entire polyline or loop. All vertices of the first edge are
+ // appended, but the first vertex of each subsequent edge is omitted (and
+ // must match the last vertex of the previous edge).
+ //
+ // If the given projection has one or more coordinate axes that "wrap", then
+ // every vertex's coordinates will be as close as possible to the previous
+ // vertex's coordinates. Note that this may yield vertices whose
+ // coordinates are outside the usual range. For example, tessellating the
+ // edge (0:170, 0:-170) (in lat:lng notation) yields (0:170, 0:190).
+ void AppendProjected(const S2Point& a, const S2Point& b,
+ std::vector<R2Point>* vertices) const;
+
+ // Converts the planar edge AB in the given projection to a chain of
+ // spherical geodesic edges and appends the vertices to "vertices".
+ //
+ // This method can be called multiple times with the same output vector to
+ // convert an entire polyline or loop. All vertices of the first edge are
+ // appended, but the first vertex of each subsequent edge is omitted (and is
+ // required to match that last vertex of the previous edge).
+ //
+ // Note that to construct an S2Loop, you must call vertices->pop_back() at
+ // the very end to eliminate the duplicate first and last vertex. Note also
+ // that if the given projection involves coordinate "wrapping" (e.g. across
+ // the 180 degree meridian) then the first and last vertices may not be
+ // exactly the same.
+ void AppendUnprojected(const R2Point& a, const R2Point& b,
+ std::vector<S2Point>* vertices) const;
+
+ // Returns the minimum supported tolerance (which corresponds to a distance
+ // less than one micrometer on the Earth's surface).
+ static S1Angle kMinTolerance();
+
+ private:
+ S1ChordAngle EstimateMaxError(const R2Point& pa, const S2Point& a,
+ const R2Point& pb, const S2Point& b) const;
+
+ void AppendUnprojected(const R2Point& pa, const S2Point& a,
+ const R2Point& pb, const S2Point& b,
+ std::vector<S2Point>* vertices) const;
+
+ void AppendProjected(const R2Point& pa, const S2Point& a,
+ const R2Point& pb, const S2Point& b,
+ std::vector<R2Point>* vertices) const;
+
+ const S2::Projection& proj_;
+
+ // The given tolerance scaled by a constant fraction so that it can be
+ // compared against the result returned by EstimateMaxError().
+ S1ChordAngle scaled_tolerance_;
+};
+
+#endif // S2_S2EDGE_TESSELLATOR_H_
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2EDGE_VECTOR_SHAPE_H_
+#define S2_S2EDGE_VECTOR_SHAPE_H_
+
+#include <utility>
+#include <vector>
+#include "s2/s2shape.h"
+
+// S2EdgeVectorShape is an S2Shape representing an arbitrary set of edges. It
+// is mainly used for testing, but it can also be useful if you have, say, a
+// collection of polylines and don't care about memory efficiency (since this
+// class would store most of the vertices twice).
+//
+// Note that if you already have data stored in an S2Loop, S2Polyline, or
+// S2Polygon, then you would be better off using the "Shape" class defined
+// within those classes (e.g., S2Loop::Shape). Similarly, if the vertex data
+// is stored in your own data structures, you can easily write your own
+// subclass of S2Shape that points to the existing vertex data rather than
+// copying it.
+class S2EdgeVectorShape : public S2Shape {
+ public:
+ // Constructs an empty edge vector.
+ S2EdgeVectorShape() {}
+
+ // Constructs an S2EdgeVectorShape from a vector of edges.
+ explicit S2EdgeVectorShape(std::vector<std::pair<S2Point, S2Point>> edges) {
+ edges_ = std::move(edges);
+ }
+
+ // Creates an S2EdgeVectorShape containing a single edge.
+ S2EdgeVectorShape(const S2Point& a, const S2Point& b) {
+ edges_.push_back(std::make_pair(a, b));
+ }
+
+ ~S2EdgeVectorShape() override = default;
+
+ // Adds an edge to the vector.
+ //
+ // IMPORTANT: This method should only be called *before* adding the
+ // S2EdgeVectorShape to an S2ShapeIndex. S2Shapes can only be modified by
+ // removing them from the index, making changes, and adding them back again.
+ void Add(const S2Point& a, const S2Point& b) {
+ edges_.push_back(std::make_pair(a, b));
+ }
+
+ // S2Shape interface:
+ int num_edges() const final { return static_cast<int>(edges_.size()); }
+ Edge edge(int e) const final {
+ return Edge(edges_[e].first, edges_[e].second);
+ }
+ int dimension() const final { return 1; }
+ ReferencePoint GetReferencePoint() const final {
+ return ReferencePoint::Contained(false);
+ }
+ int num_chains() const final { return static_cast<int>(edges_.size()); }
+ Chain chain(int i) const final { return Chain(i, 1); }
+ Edge chain_edge(int i, int j) const final {
+ S2_DCHECK_EQ(j, 0);
+ return Edge(edges_[i].first, edges_[i].second);
+ }
+ ChainPosition chain_position(int e) const final {
+ return ChainPosition(e, 0);
+ }
+
+ private:
+ std::vector<std::pair<S2Point, S2Point>> edges_;
+};
+
+#endif // S2_S2EDGE_VECTOR_SHAPE_H_
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// S2Error is a simple class consisting of an error code and a human-readable
+// error message.
+
+#ifndef S2_S2ERROR_H_
+#define S2_S2ERROR_H_
+
+#include <cstdarg>
+#include <ostream>
+#include <string>
+
+#include "s2/base/port.h"
+
+// This class is intended to be copied by value as desired. It uses
+// the default copy constructor and assignment operator.
+class S2Error {
+ public:
+ enum Code {
+ OK = 0, // No error.
+
+ ////////////////////////////////////////////////////////////////////
+ // Generic errors, not specific to geometric objects:
+
+ UNKNOWN = 1000, // Unknown error.
+ UNIMPLEMENTED = 1001, // Operation is not implemented.
+ OUT_OF_RANGE = 1002, // Argument is out of range.
+ INVALID_ARGUMENT = 1003, // Invalid argument (other than a range error).
+ FAILED_PRECONDITION = 1004, // Object is not in the required state.
+ INTERNAL = 1005, // An internal invariant has failed.
+ DATA_LOSS = 1006, // Data loss or corruption.
+ RESOURCE_EXHAUSTED = 1007, // A resource has been exhausted.
+
+ ////////////////////////////////////////////////////////////////////
+ // Error codes in the following range can be defined by clients:
+
+ USER_DEFINED_START = 1000000,
+ USER_DEFINED_END = 9999999,
+
+ ////////////////////////////////////////////////////////////////////
+ // Errors that apply to more than one type of geometry:
+
+ NOT_UNIT_LENGTH = 1, // Vertex is not unit length.
+ DUPLICATE_VERTICES = 2, // There are two identical vertices.
+ ANTIPODAL_VERTICES = 3, // There are two antipodal vertices.
+
+ ////////////////////////////////////////////////////////////////////
+ // S2Loop errors:
+
+ LOOP_NOT_ENOUGH_VERTICES = 100, // Loop with fewer than 3 vertices.
+ LOOP_SELF_INTERSECTION = 101, // Loop has a self-intersection.
+
+ ////////////////////////////////////////////////////////////////////
+ // S2Polygon errors:
+
+ POLYGON_LOOPS_SHARE_EDGE = 200, // Two polygon loops share an edge.
+ POLYGON_LOOPS_CROSS = 201, // Two polygon loops cross.
+ POLYGON_EMPTY_LOOP = 202, // Polygon has an empty loop.
+ POLYGON_EXCESS_FULL_LOOP = 203, // Non-full polygon has a full loop.
+
+ // InitOriented() was called and detected inconsistent loop orientations.
+ POLYGON_INCONSISTENT_LOOP_ORIENTATIONS = 204,
+
+ // Loop depths don't correspond to any valid nesting hierarchy.
+ POLYGON_INVALID_LOOP_DEPTH = 205,
+
+ // Actual polygon nesting does not correspond to the nesting hierarchy
+ // encoded by the loop depths.
+ POLYGON_INVALID_LOOP_NESTING = 206,
+
+ ////////////////////////////////////////////////////////////////////
+ // S2Builder errors:
+
+ // The S2Builder snap function moved a vertex by more than the specified
+ // snap radius.
+ BUILDER_SNAP_RADIUS_TOO_SMALL = 300,
+
+ // S2Builder expected all edges to have siblings (as specified by
+ // S2Builder::GraphOptions::SiblingPairs::REQUIRE), but some were missing.
+ BUILDER_MISSING_EXPECTED_SIBLING_EDGES = 301,
+
+ // S2Builder found an unexpected degenerate edge. For example,
+ // Graph::GetLeftTurnMap() does not support degenerate edges.
+ BUILDER_UNEXPECTED_DEGENERATE_EDGE = 302,
+
+ // S2Builder found a vertex with (indegree != outdegree), which means
+ // that the given edges cannot be assembled into loops.
+ BUILDER_EDGES_DO_NOT_FORM_LOOPS = 303,
+
+ // The edges provided to S2Builder cannot be assembled into a polyline.
+ BUILDER_EDGES_DO_NOT_FORM_POLYLINE = 304,
+
+ // There was an attempt to assemble a polygon from degenerate geometry
+ // without having specified a predicate to decide whether the output is
+ // the empty polygon (containing no points) or the full polygon
+ // (containing all points).
+ BUILDER_IS_FULL_PREDICATE_NOT_SPECIFIED = 305,
+ };
+ S2Error() : code_(OK), text_() {}
+
+ // Set the error to the given code and printf-style message. Note that you
+ // can prepend text to an existing error by calling Init() more than once:
+ //
+ // error->Init(error->code(), "Loop %d: %s", j, error->text().c_str());
+ void Init(Code code, const char* format, ...) ABSL_PRINTF_ATTRIBUTE(3, 4);
+
+ bool ok() const { return code_ == OK; }
+ Code code() const { return code_; }
+ string text() const { return text_; }
+
+ // Clear the error to contain the OK code and no error message.
+ void Clear();
+
+ private:
+ Code code_;
+ string text_;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline std::ostream& operator<<(std::ostream& os, const S2Error& error) {
+ return os << error.text();
+}
+
+inline void S2Error::Clear() {
+ code_ = OK;
+ text_.clear();
+}
+
+#endif // S2_S2ERROR_H_
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef S2_S2FURTHEST_EDGE_QUERY_H_
+#define S2_S2FURTHEST_EDGE_QUERY_H_
+
+#include <memory>
+#include <queue>
+#include <type_traits>
+#include <vector>
+
+#include "s2/base/logging.h"
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/third_party/absl/container/inlined_vector.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/s1angle.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s2cell.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2closest_edge_query_base.h"
+#include "s2/s2edge_distances.h"
+#include "s2/s2max_distance_targets.h"
+#include "s2/s2shape_index.h"
+
+// S2FurthestEdgeQuery is a helper class for searching within an S2ShapeIndex
+// for the furthest edge(s) to a given query point, edge, S2Cell, or geometry
+// collection. The furthest edge is defined as the one which maximizes the
+// distance from any point on that edge to any point on the target geometry.
+
+// As an example, given a set of polylines, the following code efficiently
+// finds the furthest 5 edges to a query point:
+//
+// void Test(const vector<S2Polyline*>& polylines, const S2Point& point) {
+// MutableS2ShapeIndex index;
+// for (S2Polyline* polyline : polylines) {
+// index.Add(new S2Polyline::Shape(polyline));
+// }
+// S2FurthestEdgeQuery query(&index);
+// query.mutable_options()->set_max_results(5);
+// S2FurthestEdgeQuery::PointTarget target(point);
+// for (const auto& result : query.FindFurthestEdges(&target)) {
+// // The Result struct contains the following accessors:
+// // "distance()" is the distance to the edge.
+// // "shape_id()" identifies the S2Shape containing the edge.
+// // "edge_id()" identifies the edge with the given shape.
+// // The following convenience methods may also be useful:
+// // query.GetEdge(result) returns the endpoints of the edge.
+// int polyline_index = result.shape_id();
+// int edge_index = result.edge_id();
+// S1ChordAngle distance = result.distance();
+// S2Shape::Edge edge = query.GetEdge(result);
+// }
+// }
+//
+// You can find either the k furthest edges, or all edges no closer than a
+// given radius, or both (i.e., the k furthest edges no closer than a given
+// minimum radius).
+// E.g. to find all the edges further than 5 kilometers, call
+//
+// query.mutable_options()->set_min_distance(
+// S2Earth::ToAngle(util::units::Kilometers(5)));
+//
+// By default *all* edges are returned, so you should always specify either
+// max_results() or min_distance() or both. Setting min distance may not be
+// very restrictive, so strongly consider using max_results(). There is also a
+// FindFurthestEdge() convenience method that returns only the single furthest
+// edge.
+//
+// Note that by default, distances are measured to the boundary and interior
+// of polygons. To change this behavior, call set_include_interiors(false).
+//
+// If you only need to test whether the distance is above or below a given
+// threshold (e.g., 10 km), you can use the IsDistanceGreater() method. This
+// is much faster than actually calculating the distance with
+// FindFurthestEdge(), since the implementation can stop as soon as it can
+// prove that the maximum distance is either above or below the threshold.
+//
+// To find the furthest edges to a query edge rather than a point, use:
+//
+// S2FurthestEdgeQuery::EdgeTarget target(v0, v1);
+// query.FindFurthestEdges(&target);
+//
+// Similarly you can find the furthest edges to an S2Cell by using an
+// S2FurthestEdgeQuery::CellTarget, and you can find the furthest edges to an
+// arbitrary collection of points, polylines, and polygons by using an
+// S2FurthestEdgeQuery::ShapeIndexTarget.
+//
+// The implementation is designed to be fast for both simple and complex
+// geometric objects.
+class S2FurthestEdgeQuery {
+ public:
+ // See S2FurthestEdgeQueryBase for full documentation.
+
+ // S2MaxDistance is a thin wrapper around S1ChordAngle that implements the
+ // Distance concept required by S2ClosestEdgeQueryBase. It inverts the sense
+ // of some methods to enable the closest edge query to return furthest
+ // edges.
+ // Distance and Base are made private to prevent leakage outside of this
+ // class. The private and public sections are interleaved since the public
+ // options class needs the private Base class.
+ private:
+ using Distance = S2MaxDistance;
+ using Base = S2ClosestEdgeQueryBase<Distance>;
+
+ public:
+ // Options that control the set of edges returned. Note that by default
+ // *all* edges are returned, so you will always want to set either the
+ // max_results() option or the min_distance() option (or both).
+ class Options : public Base::Options {
+ public:
+ // See S2ClosestEdgeQueryBase::Options for the full set of options.
+ Options();
+
+ // The following methods are like the corresponding max_distance() methods
+ // in S2ClosestEdgeQuery, except that they specify the minimum distance to
+ // the target.
+ S1ChordAngle min_distance() const;
+ void set_min_distance(S1ChordAngle min_distance);
+ void set_inclusive_min_distance(S1ChordAngle min_distance);
+ void set_conservative_min_distance(S1ChordAngle min_distance);
+
+ void set_min_distance(S1Angle min_distance);
+ void set_inclusive_min_distance(S1Angle min_distance);
+ void set_conservative_min_distance(S1Angle min_distance);
+
+ // Versions of set_max_error() that accept S1ChordAngle / S1Angle.
+ // (See S2ClosestEdgeQueryBaseOptions for details.)
+ S1ChordAngle max_error() const;
+ void set_max_error(S1ChordAngle max_error);
+ void set_max_error(S1Angle max_error);
+
+ // Inherited options (see s2closestedgequerybase.h for details):
+ using Base::Options::set_max_results;
+ using Base::Options::set_include_interiors;
+ using Base::Options::set_use_brute_force;
+
+ private:
+ // The S2MaxDistance versions of these methods are not intended for
+ // public use.
+ using Base::Options::set_max_distance;
+ using Base::Options::set_max_error;
+ using Base::Options::max_distance;
+ using Base::Options::max_error;
+ };
+
+ // "Target" represents the geometry to which the distance is measured.
+ // There are subtypes for measuring the distance to a point, an edge, an
+ // S2Cell, or an S2ShapeIndex (an arbitrary collection of geometry). Note
+ // that S2DistanceTarget<Distance> is equivalent to S2MaxDistanceTarget in
+ // s2max_distance_targets, which the following subtypes
+ // (e.g. S2MaxDistancePointTarget) extend.
+ using Target = S2DistanceTarget<Distance>;
+
+ // Target subtype that computes the furthest distance to a point.
+ class PointTarget final : public S2MaxDistancePointTarget {
+ public:
+ explicit PointTarget(const S2Point& point);
+ int max_brute_force_index_size() const override;
+ };
+
+ // Target subtype that computes the furthest distance to an edge.
+ class EdgeTarget final : public S2MaxDistanceEdgeTarget {
+ public:
+ explicit EdgeTarget(const S2Point& a, const S2Point& b);
+ int max_brute_force_index_size() const override;
+ };
+
+ // Target subtype that computes the furthest distance to an S2Cell
+ // (including the interior of the cell).
+ class CellTarget final : public S2MaxDistanceCellTarget {
+ public:
+ explicit CellTarget(const S2Cell& cell);
+ int max_brute_force_index_size() const override;
+ };
+
+ // Target subtype that computes the furthest distance to an S2ShapeIndex
+ // (an arbitrary collection of points, polylines, and/or polygons).
+ //
+ // By default, distances are measured to the boundary and interior of
+ // polygons in the S2ShapeIndex rather than to polygon boundaries only.
+ // If you wish to change this behavior, you may call
+ //
+ // target.set_include_interiors(false);
+ //
+ // (see S2MaxDistanceShapeIndexTarget for details).
+ class ShapeIndexTarget final : public S2MaxDistanceShapeIndexTarget {
+ public:
+ explicit ShapeIndexTarget(const S2ShapeIndex* index);
+ int max_brute_force_index_size() const override;
+ };
+
+ // Result class to pass back to user. We choose to pass back this result
+ // type, which has an S1ChordAngle as its distance, rather than the
+ // Base::Result returned from the query which uses S2MaxDistance.
+ class Result {
+ public:
+ // The default constructor, which yields an invalid result.
+ Result() : Result(S1ChordAngle::Negative(), -1, -1) {}
+
+ // Construct a Result from a Base::Result.
+ explicit Result(const Base::Result& base)
+ : Result(S1ChordAngle(base.distance()), base.shape_id(),
+ base.edge_id()) {}
+
+ // Constructs a Result object for the given edge with the given distance.
+ Result(S1ChordAngle distance, int32 _shape_id, int32 _edge_id)
+ : distance_(distance), shape_id_(_shape_id), edge_id_(_edge_id) {}
+
+ // Returns true if this Result object does not refer to any edge.
+ // (The only case where an empty Result is returned is when the
+ // FindFurthestEdges() method does not find any edges that meet the
+ // specified criteria.)
+ bool is_empty() const { return edge_id_ < 0; }
+
+ // The distance from the target to this point.
+ S1ChordAngle distance() const { return distance_; }
+
+ // The edge identifiers.
+ int32 shape_id() const { return shape_id_; }
+ int32 edge_id() const { return edge_id_; }
+
+ // Returns true if two Result objects are identical.
+ friend bool operator==(const Result& x, const Result& y) {
+ return (x.distance_ == y.distance_ &&
+ x.shape_id_ == y.shape_id_ &&
+ x.edge_id_ == y.edge_id_);
+ }
+
+ // Compares edges first by distance, then by (shape_id, edge_id).
+ friend bool operator<(const Result& x, const Result& y) {
+ if (x.distance_ < y.distance_) return true;
+ if (y.distance_ < x.distance_) return false;
+ if (x.shape_id_ < y.shape_id_) return true;
+ if (y.shape_id_ < x.shape_id_) return false;
+ return x.edge_id_ < y.edge_id_;
+ }
+
+ private:
+ S1ChordAngle distance_;
+ int32 shape_id_; // Identifies an indexed shape.
+ int32 edge_id_; // Identifies an edge within the shape.
+ };
+
+ // Convenience constructor that calls Init(). Options may be specified here
+ // or changed at any time using the mutable_options() accessor method.
+ explicit S2FurthestEdgeQuery(const S2ShapeIndex* index,
+ const Options& options = Options());
+
+ // Default constructor; requires Init() to be called.
+ S2FurthestEdgeQuery();
+ ~S2FurthestEdgeQuery();
+
+ S2FurthestEdgeQuery(const S2FurthestEdgeQuery&) = delete;
+ void operator=(const S2FurthestEdgeQuery&) = delete;
+
+ // Initializes the query. Options may be specified here or changed at any
+ // time using the mutable_options() accessor method.
+ //
+ // REQUIRES: "index" must persist for the lifetime of this object.
+ // REQUIRES: ReInit() must be called if "index" is modified.
+ void Init(const S2ShapeIndex* index, const Options& options = Options());
+
+ // Reinitializes the query. This method must be called whenever the
+ // underlying S2ShapeIndex is modified.
+ void ReInit();
+
+ // Returns a reference to the underlying S2ShapeIndex.
+ const S2ShapeIndex& index() const;
+
+ // Returns the query options. Options can be modified between queries.
+ const Options& options() const;
+ Options* mutable_options();
+
+ // Returns the furthest edges to the given target that satisfy the given
+ // options. This method may be called multiple times.
+ std::vector<Result> FindFurthestEdges(Target* target);
+
+ // This version can be more efficient when this method is called many times,
+ // since it does not require allocating a new vector on each call.
+ void FindFurthestEdges(Target* target, std::vector<Result>* results);
+
+ //////////////////////// Convenience Methods ////////////////////////
+
+ // Returns the furthest edge to the target. If no edge satisfies the search
+ // criteria, then the Result object will have
+ // distance == S1ChordAngle::Negative() and shape_id == edge_id == -1.
+ Result FindFurthestEdge(Target* target);
+
+ // Returns the maximum distance to the target. If the index or target is
+ // empty, returns S1ChordAngle::Negative().
+ //
+ // Use IsDistanceGreater() if you only want to compare the distance against a
+ // threshold value, since it is often much faster.
+ S1ChordAngle GetDistance(Target* target);
+
+ // Returns true if the distance to "target" is greater than "limit".
+ //
+ // This method is usually much faster than GetDistance(), since it is much
+ // less work to determine whether the maximum distance is above or below a
+ // threshold than it is to calculate the actual maximum distance.
+ bool IsDistanceGreater(Target* target, S1ChordAngle limit);
+
+ // Like IsDistanceGreater(), but also returns true if the distance to
+ // "target" is exactly equal to "limit".
+ bool IsDistanceGreaterOrEqual(Target* target, S1ChordAngle limit);
+
+ // Like IsDistanceGreaterOrEqual(), except that "limit" is decreased by the
+ // maximum error in the distance calculation. This ensures that this
+ // function returns true whenever the true, exact distance is greater than
+ // or equal to "limit".
+ bool IsConservativeDistanceGreaterOrEqual(Target* target,
+ S1ChordAngle limit);
+
+ // Returns the endpoints of the given result edge.
+ // REQUIRES: result.edge_id >= 0
+ S2Shape::Edge GetEdge(const Result& result) const;
+
+ private:
+ Options options_;
+ Base base_;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+inline S2FurthestEdgeQuery::Options::Options() {
+}
+
+inline S1ChordAngle S2FurthestEdgeQuery::Options::min_distance() const {
+ return S1ChordAngle(max_distance());
+}
+
+inline void S2FurthestEdgeQuery::Options::set_min_distance(
+ S1ChordAngle min_distance) {
+ Base::Options::set_max_distance(Distance(min_distance));
+}
+
+inline void S2FurthestEdgeQuery::Options::set_min_distance(
+ S1Angle min_distance) {
+ Base::Options::set_max_distance(Distance(S1ChordAngle(min_distance)));
+}
+
+inline void S2FurthestEdgeQuery::Options::set_inclusive_min_distance(
+ S1ChordAngle min_distance) {
+ set_min_distance(min_distance.Predecessor());
+}
+
+inline void S2FurthestEdgeQuery::Options::set_inclusive_min_distance(
+ S1Angle min_distance) {
+ set_inclusive_min_distance(S1ChordAngle(min_distance));
+}
+
+inline S1ChordAngle S2FurthestEdgeQuery::Options::max_error() const {
+ return S1ChordAngle(Base::Options::max_error());
+}
+
+inline void S2FurthestEdgeQuery::Options::set_max_error(
+ S1ChordAngle max_error) {
+ Base::Options::set_max_error(max_error);
+}
+
+inline void S2FurthestEdgeQuery::Options::set_max_error(S1Angle max_error) {
+ Base::Options::set_max_error(S1ChordAngle(max_error));
+}
+
+inline S2FurthestEdgeQuery::PointTarget::PointTarget(const S2Point& point)
+ : S2MaxDistancePointTarget(point) {
+}
+
+inline S2FurthestEdgeQuery::EdgeTarget::EdgeTarget(const S2Point& a,
+ const S2Point& b)
+ : S2MaxDistanceEdgeTarget(a, b) {
+}
+
+inline S2FurthestEdgeQuery::CellTarget::CellTarget(const S2Cell& cell)
+ : S2MaxDistanceCellTarget(cell) {
+}
+
+inline S2FurthestEdgeQuery::ShapeIndexTarget::ShapeIndexTarget(
+ const S2ShapeIndex* index)
+ : S2MaxDistanceShapeIndexTarget(index) {
+}
+
+inline S2FurthestEdgeQuery::S2FurthestEdgeQuery(const S2ShapeIndex* index,
+ const Options& options) {
+ Init(index, options);
+}
+
+inline void S2FurthestEdgeQuery::Init(const S2ShapeIndex* index,
+ const Options& options) {
+ options_ = options;
+ base_.Init(index);
+}
+
+inline void S2FurthestEdgeQuery::ReInit() {
+ base_.ReInit();
+}
+
+inline const S2ShapeIndex& S2FurthestEdgeQuery::index() const {
+ return base_.index();
+}
+
+inline const S2FurthestEdgeQuery::Options&
+ S2FurthestEdgeQuery::options() const {
+ return options_;
+}
+
+inline S2FurthestEdgeQuery::Options* S2FurthestEdgeQuery::mutable_options() {
+ return &options_;
+}
+
+inline std::vector<S2FurthestEdgeQuery::Result>
+S2FurthestEdgeQuery::FindFurthestEdges(Target* target) {
+ std::vector<S2FurthestEdgeQuery::Result> results;
+ FindFurthestEdges(target, &results);
+ return results;
+}
+
+inline S1ChordAngle S2FurthestEdgeQuery::GetDistance(Target* target) {
+ return FindFurthestEdge(target).distance();
+}
+
+inline S2Shape::Edge S2FurthestEdgeQuery::GetEdge(const Result& result) const {
+ return index().shape(result.shape_id())->edge(result.edge_id());
+}
+
+#endif // S2_S2FURTHEST_EDGE_QUERY_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2LATLNG_H_
+#define S2_S2LATLNG_H_
+
+#include <cmath>
+#include <iosfwd>
+#include <ostream>
+#include <string>
+
+#include "s2/base/integral_types.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/r2.h"
+#include "s2/s1angle.h"
+#include "s2/util/math/vector.h"
+
+// This class represents a point on the unit sphere as a pair
+// of latitude-longitude coordinates. Like the rest of the "geometry"
+// package, the intent is to represent spherical geometry as a mathematical
+// abstraction, so functions that are specifically related to the Earth's
+// geometry (e.g. easting/northing conversions) should be put elsewhere.
+//
+// This class is intended to be copied by value as desired. It uses
+// the default copy constructor and assignment operator.
+class S2LatLng {
+ public:
+ // Constructor. The latitude and longitude are allowed to be outside
+ // the is_valid() range. However, note that most methods that accept
+ // S2LatLngs expect them to be normalized (see Normalized() below).
+ S2LatLng(S1Angle lat, S1Angle lng);
+
+ // The default constructor sets the latitude and longitude to zero. This is
+ // mainly useful when declaring arrays, STL containers, etc.
+ S2LatLng();
+
+ // Convert a direction vector (not necessarily unit length) to an S2LatLng.
+ explicit S2LatLng(const S2Point& p);
+
+ // Returns an S2LatLng for which is_valid() will return false.
+ static S2LatLng Invalid();
+
+ // Convenience functions -- shorter than calling S1Angle::Radians(), etc.
+ static S2LatLng FromRadians(double lat_radians, double lng_radians);
+ static S2LatLng FromDegrees(double lat_degrees, double lng_degrees);
+ static S2LatLng FromE5(int32 lat_e5, int32 lng_e5);
+ static S2LatLng FromE6(int32 lat_e6, int32 lng_e6);
+ static S2LatLng FromE7(int32 lat_e7, int32 lng_e7);
+
+ // Convenience functions -- to use when args have been fixed32s in protos.
+ //
+ // The arguments are static_cast into int32, so very large unsigned values
+ // are treated as negative numbers.
+ static S2LatLng FromUnsignedE6(uint32 lat_e6, uint32 lng_e6);
+ static S2LatLng FromUnsignedE7(uint32 lat_e7, uint32 lng_e7);
+
+ // Methods to compute the latitude and longitude of a point separately.
+ static S1Angle Latitude(const S2Point& p);
+ static S1Angle Longitude(const S2Point& p);
+
+ // Accessor methods.
+ S1Angle lat() const { return S1Angle::Radians(coords_[0]); }
+ S1Angle lng() const { return S1Angle::Radians(coords_[1]); }
+ const R2Point& coords() const { return coords_; }
+
+ // Return true if the latitude is between -90 and 90 degrees inclusive
+ // and the longitude is between -180 and 180 degrees inclusive.
+ bool is_valid() const;
+
+ // Clamps the latitude to the range [-90, 90] degrees, and adds or subtracts
+ // a multiple of 360 degrees to the longitude if necessary to reduce it to
+ // the range [-180, 180].
+ S2LatLng Normalized() const;
+
+ // Converts a normalized S2LatLng to the equivalent unit-length vector.
+ // The maximum error in the result is 1.5 * DBL_EPSILON. (This does not
+ // include the error of converting degrees, E5, E6, or E7 to radians.)
+ //
+ // Can be used just like an S2Point constructor. For example:
+ // S2Cap cap;
+ // cap.AddPoint(S2Point(latlng));
+ explicit operator S2Point() const;
+
+ // Converts to an S2Point (equivalent to the operator above).
+ S2Point ToPoint() const;
+
+ // Returns the distance (measured along the surface of the sphere) to the
+ // given S2LatLng, implemented using the Haversine formula. This is
+ // equivalent to
+ //
+ // S1Angle(ToPoint(), o.ToPoint())
+ //
+ // except that this function is slightly faster, and is also somewhat less
+ // accurate for distances approaching 180 degrees (see s1angle.h for
+ // details). Both S2LatLngs must be normalized.
+ S1Angle GetDistance(const S2LatLng& o) const;
+
+ // Simple arithmetic operations for manipulating latitude-longitude pairs.
+ // The results are not normalized (see Normalized()).
+ friend S2LatLng operator+(const S2LatLng& a, const S2LatLng& b);
+ friend S2LatLng operator-(const S2LatLng& a, const S2LatLng& b);
+ friend S2LatLng operator*(double m, const S2LatLng& a);
+ friend S2LatLng operator*(const S2LatLng& a, double m);
+
+ bool operator==(const S2LatLng& o) const { return coords_ == o.coords_; }
+ bool operator!=(const S2LatLng& o) const { return coords_ != o.coords_; }
+ bool operator<(const S2LatLng& o) const { return coords_ < o.coords_; }
+ bool operator>(const S2LatLng& o) const { return coords_ > o.coords_; }
+ bool operator<=(const S2LatLng& o) const { return coords_ <= o.coords_; }
+ bool operator>=(const S2LatLng& o) const { return coords_ >= o.coords_; }
+
+ bool ApproxEquals(const S2LatLng& o,
+ S1Angle max_error = S1Angle::Radians(1e-15)) const {
+ return coords_.aequal(o.coords_, max_error.radians());
+ }
+
+ // Exports the latitude and longitude in degrees, separated by a comma.
+ // e.g. "94.518000,150.300000"
+ string ToStringInDegrees() const;
+ void ToStringInDegrees(string* s) const;
+
+ private:
+ // Internal constructor.
+ explicit S2LatLng(const R2Point& coords) : coords_(coords) {}
+
+ // This is internal to avoid ambiguity about which units are expected.
+ S2LatLng(double lat_radians, double lng_radians)
+ : coords_(lat_radians, lng_radians) {}
+
+ R2Point coords_;
+};
+
+// Hasher for S2LatLng.
+// Example use: std::unordered_map<S2LatLng, int, S2LatLngHash>.
+struct S2LatLngHash {
+ size_t operator()(const S2LatLng& lat_lng) const {
+ return GoodFastHash<R2Point>()(lat_lng.coords());
+ }
+};
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline S2LatLng::S2LatLng(S1Angle lat, S1Angle lng)
+ : coords_(lat.radians(), lng.radians()) {}
+
+inline S2LatLng::S2LatLng() : coords_(0, 0) {}
+
+inline S2LatLng S2LatLng::FromRadians(double lat_radians, double lng_radians) {
+ return S2LatLng(lat_radians, lng_radians);
+}
+
+inline S2LatLng S2LatLng::FromDegrees(double lat_degrees, double lng_degrees) {
+ return S2LatLng(S1Angle::Degrees(lat_degrees), S1Angle::Degrees(lng_degrees));
+}
+
+inline S2LatLng S2LatLng::FromE5(int32 lat_e5, int32 lng_e5) {
+ return S2LatLng(S1Angle::E5(lat_e5), S1Angle::E5(lng_e5));
+}
+
+inline S2LatLng S2LatLng::FromE6(int32 lat_e6, int32 lng_e6) {
+ return S2LatLng(S1Angle::E6(lat_e6), S1Angle::E6(lng_e6));
+}
+
+inline S2LatLng S2LatLng::FromE7(int32 lat_e7, int32 lng_e7) {
+ return S2LatLng(S1Angle::E7(lat_e7), S1Angle::E7(lng_e7));
+}
+
+inline S2LatLng S2LatLng::FromUnsignedE6(uint32 lat_e6, uint32 lng_e6) {
+ return S2LatLng(S1Angle::UnsignedE6(lat_e6), S1Angle::UnsignedE6(lng_e6));
+}
+
+inline S2LatLng S2LatLng::FromUnsignedE7(uint32 lat_e7, uint32 lng_e7) {
+ return S2LatLng(S1Angle::UnsignedE7(lat_e7), S1Angle::UnsignedE7(lng_e7));
+}
+
+inline S2LatLng S2LatLng::Invalid() {
+ // These coordinates are outside the bounds allowed by is_valid().
+ return S2LatLng(M_PI, 2 * M_PI);
+}
+
+inline S1Angle S2LatLng::Latitude(const S2Point& p) {
+ // We use atan2 rather than asin because the input vector is not necessarily
+ // unit length, and atan2 is much more accurate than asin near the poles.
+ return S1Angle::Radians(atan2(p[2], sqrt(p[0]*p[0] + p[1]*p[1])));
+}
+
+inline S1Angle S2LatLng::Longitude(const S2Point& p) {
+ // Note that atan2(0, 0) is defined to be zero.
+ return S1Angle::Radians(atan2(p[1], p[0]));
+}
+
+inline bool S2LatLng::is_valid() const {
+ return (std::fabs(lat().radians()) <= M_PI_2 &&
+ std::fabs(lng().radians()) <= M_PI);
+}
+
+inline S2LatLng::operator S2Point() const {
+ return ToPoint();
+}
+
+inline S2LatLng operator+(const S2LatLng& a, const S2LatLng& b) {
+ return S2LatLng(a.coords_ + b.coords_);
+}
+
+inline S2LatLng operator-(const S2LatLng& a, const S2LatLng& b) {
+ return S2LatLng(a.coords_ - b.coords_);
+}
+
+inline S2LatLng operator*(double m, const S2LatLng& a) {
+ return S2LatLng(m * a.coords_);
+}
+
+inline S2LatLng operator*(const S2LatLng& a, double m) {
+ return S2LatLng(m * a.coords_);
+}
+
+std::ostream& operator<<(std::ostream& os, const S2LatLng& ll);
+
+#endif // S2_S2LATLNG_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2LATLNG_RECT_H_
+#define S2_S2LATLNG_RECT_H_
+
+#include <cmath>
+#include <iosfwd>
+#include <iostream>
+
+#include "s2/base/logging.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/r1interval.h"
+#include "s2/s1angle.h"
+#include "s2/s1interval.h"
+#include "s2/s2latlng.h"
+#include "s2/s2region.h"
+
+class Decoder;
+class Encoder;
+class S2Cap;
+class S2Cell;
+
+// An S2LatLngRect represents a closed latitude-longitude rectangle. It is
+// capable of representing the empty and full rectangles as well as single
+// points. Note that the latitude-longitude space is considered to have a
+// *cylindrical* topology rather than a spherical one, i.e. the poles have
+// multiple lat/lng representations. An S2LatLngRect may be defined so that
+// includes some representations of a pole but not others. Use the
+// PolarClosure() method if you want to expand a rectangle so that it contains
+// all possible representations of any contained poles.
+//
+// Because S2LatLngRect uses S1Interval to store the longitude range,
+// longitudes of -180 degrees are treated specially. Except for empty
+// and full longitude spans, -180 degree longitudes will turn into +180
+// degrees. This sign flip causes lng_lo() to be greater than lng_hi(),
+// indicating that the rectangle will wrap around through -180 instead of
+// through +179. Thus the math is consistent within the library, but the sign
+// flip can be surprising, especially when working with map projections where
+// -180 and +180 are at opposite ends of the flattened map. See the comments
+// on S1Interval for more details.
+//
+// This class is intended to be copied by value as desired. It uses
+// the default copy constructor and assignment operator, however it is
+// not a "plain old datatype" (POD) because it has virtual functions.
+class S2LatLngRect final : public S2Region {
+ public:
+ // Construct a rectangle from minimum and maximum latitudes and longitudes.
+ // If lo.lng() > hi.lng(), the rectangle spans the 180 degree longitude
+ // line. Both points must be normalized, with lo.lat() <= hi.lat().
+ // The rectangle contains all the points p such that 'lo' <= p <= 'hi',
+ // where '<=' is defined in the obvious way.
+ S2LatLngRect(const S2LatLng& lo, const S2LatLng& hi);
+
+ // Construct a rectangle from latitude and longitude intervals. The two
+ // intervals must either be both empty or both non-empty, and the latitude
+ // interval must not extend outside [-90, +90] degrees.
+ // Note that both intervals (and hence the rectangle) are closed.
+ S2LatLngRect(const R1Interval& lat, const S1Interval& lng);
+
+ // The default constructor creates an empty S2LatLngRect.
+ S2LatLngRect();
+
+ // Construct a rectangle of the given size centered around the given point.
+ // "center" needs to be normalized, but "size" does not. The latitude
+ // interval of the result is clamped to [-90,90] degrees, and the longitude
+ // interval of the result is Full() if and only if the longitude size is
+ // 360 degrees or more. Examples of clamping (in degrees):
+ //
+ // center=(80,170), size=(40,60) -> lat=[60,90], lng=[140,-160]
+ // center=(10,40), size=(210,400) -> lat=[-90,90], lng=[-180,180]
+ // center=(-90,180), size=(20,50) -> lat=[-90,-80], lng=[155,-155]
+ static S2LatLngRect FromCenterSize(const S2LatLng& center,
+ const S2LatLng& size);
+
+ // Construct a rectangle containing a single (normalized) point.
+ static S2LatLngRect FromPoint(const S2LatLng& p);
+
+ // Construct the minimal bounding rectangle containing the two given
+ // normalized points. This is equivalent to starting with an empty
+ // rectangle and calling AddPoint() twice. Note that it is different than
+ // the S2LatLngRect(lo, hi) constructor, where the first point is always
+ // used as the lower-left corner of the resulting rectangle.
+ static S2LatLngRect FromPointPair(const S2LatLng& p1, const S2LatLng& p2);
+
+ // Accessor methods.
+ S1Angle lat_lo() const { return S1Angle::Radians(lat_.lo()); }
+ S1Angle lat_hi() const { return S1Angle::Radians(lat_.hi()); }
+ S1Angle lng_lo() const { return S1Angle::Radians(lng_.lo()); }
+ S1Angle lng_hi() const { return S1Angle::Radians(lng_.hi()); }
+ const R1Interval& lat() const { return lat_; }
+ const S1Interval& lng() const { return lng_; }
+ R1Interval *mutable_lat() { return &lat_; }
+ S1Interval *mutable_lng() { return &lng_; }
+ S2LatLng lo() const { return S2LatLng(lat_lo(), lng_lo()); }
+ S2LatLng hi() const { return S2LatLng(lat_hi(), lng_hi()); }
+
+ // The canonical empty and full rectangles, as derived from the Empty
+ // and Full R1 and S1 Intervals.
+ // Empty: lat_lo=1, lat_hi=0, lng_lo=Pi, lng_hi=-Pi (radians)
+ static S2LatLngRect Empty();
+ // Full: lat_lo=-Pi/2, lat_hi=Pi/2, lng_lo=-Pi, lng_hi=Pi (radians)
+ static S2LatLngRect Full();
+
+ // The full allowable range of latitudes and longitudes.
+ static R1Interval FullLat() { return R1Interval(-M_PI_2, M_PI_2); }
+ static S1Interval FullLng() { return S1Interval::Full(); }
+
+ // Returns true if the rectangle is valid, which essentially just means
+ // that the latitude bounds do not exceed Pi/2 in absolute value and
+ // the longitude bounds do not exceed Pi in absolute value. Also, if
+ // either the latitude or longitude bound is empty then both must be.
+ bool is_valid() const;
+
+ // Returns true if the rectangle is empty, i.e. it contains no points at all.
+ bool is_empty() const;
+
+ // Returns true if the rectangle is full, i.e. it contains all points.
+ bool is_full() const;
+
+ // Returns true if the rectangle is a point, i.e. lo() == hi()
+ bool is_point() const;
+
+ // Returns true if lng_.lo() > lng_.hi(), i.e. the rectangle crosses
+ // the 180 degree longitude line.
+ bool is_inverted() const { return lng_.is_inverted(); }
+
+ // Returns the k-th vertex of the rectangle (k = 0,1,2,3) in CCW order
+ // (lower left, lower right, upper right, upper left). For convenience, the
+ // argument is reduced modulo 4 to the range [0..3].
+ S2LatLng GetVertex(int k) const;
+
+ // Returns the center of the rectangle in latitude-longitude space
+ // (in general this is not the center of the region on the sphere).
+ S2LatLng GetCenter() const;
+
+ // Returns the width and height of this rectangle in latitude-longitude
+ // space. Empty rectangles have a negative width and height.
+ S2LatLng GetSize() const;
+
+ // Returns the surface area of this rectangle on the unit sphere.
+ double Area() const;
+
+ // Returns the true centroid of the rectangle multiplied by its surface area
+ // (see s2centroids.h for details on centroids). The result is not unit
+ // length, so you may want to normalize it. Note that in general the
+ // centroid is *not* at the center of the rectangle, and in fact it may not
+ // even be contained by the rectangle. (It is the "center of mass" of the
+ // rectangle viewed as subset of the unit sphere, i.e. it is the point in
+ // space about which this curved shape would rotate.)
+ //
+ // The reason for multiplying the result by the rectangle area is to make it
+ // easier to compute the centroid of more complicated shapes. The centroid
+ // of a union of disjoint regions can be computed simply by adding their
+ // GetCentroid() results.
+ S2Point GetCentroid() const;
+
+ // More efficient version of Contains() that accepts a S2LatLng rather than
+ // an S2Point. The argument must be normalized.
+ bool Contains(const S2LatLng& ll) const;
+
+ // Returns true if and only if the given point is contained in the interior
+ // of the region (i.e. the region excluding its boundary). The point 'p'
+ // does not need to be normalized.
+ bool InteriorContains(const S2Point& p) const;
+
+ // More efficient version of InteriorContains() that accepts a S2LatLng
+ // rather than an S2Point. The argument must be normalized.
+ bool InteriorContains(const S2LatLng& ll) const;
+
+ // Returns true if and only if the rectangle contains the given other
+ // rectangle.
+ bool Contains(const S2LatLngRect& other) const;
+
+ // Returns true if and only if the interior of this rectangle contains all
+ // points of the given other rectangle (including its boundary).
+ bool InteriorContains(const S2LatLngRect& other) const;
+
+ // Returns true if this rectangle and the given other rectangle have any
+ // points in common.
+ bool Intersects(const S2LatLngRect& other) const;
+
+ // Returns true if this rectangle intersects the given cell. (This is an
+ // exact test and may be fairly expensive, see also MayIntersect below.)
+ bool Intersects(const S2Cell& cell) const;
+
+ // Returns true if and only if the interior of this rectangle intersects
+ // any point (including the boundary) of the given other rectangle.
+ bool InteriorIntersects(const S2LatLngRect& other) const;
+
+ // Returns true if the boundary of this rectangle intersects the given
+ // geodesic edge (v0, v1).
+ bool BoundaryIntersects(const S2Point& v0, const S2Point& v1) const;
+
+ // Increase the size of the bounding rectangle to include the given point.
+ // The rectangle is expanded by the minimum amount possible. The S2LatLng
+ // argument must be normalized.
+ void AddPoint(const S2Point& p);
+ void AddPoint(const S2LatLng& ll);
+
+ // Returns a rectangle that has been expanded by margin.lat() on each side in
+ // the latitude direction, and by margin.lng() on each side in the longitude
+ // direction. If either margin is negative, then shrinks the rectangle on
+ // the corresponding sides instead. The resulting rectangle may be empty.
+ //
+ // As noted above, the latitude-longitude space has the topology of a
+ // cylinder. Longitudes "wrap around" at +/-180 degrees, while latitudes
+ // are clamped to range [-90, 90]. This means that any expansion (positive
+ // or negative) of the full longitude range remains full (since the
+ // "rectangle" is actually a continuous band around the cylinder), while
+ // expansion of the full latitude range remains full only if the margin is
+ // positive.
+ //
+ // If either the latitude or longitude interval becomes empty after
+ // expansion by a negative margin, the result is empty.
+ //
+ // Note that if an expanded rectangle contains a pole, it may not contain
+ // all possible lat/lng representations of that pole (see header above).
+ // Use the PolarClosure() method if you do not want this behavior.
+ //
+ // If you are trying to grow a rectangle by a certain *distance* on the
+ // sphere (e.g. 5km), use the ExpandedByDistance() method instead.
+ S2LatLngRect Expanded(const S2LatLng& margin) const;
+
+ // If the rectangle does not include either pole, returns it unmodified.
+ // Otherwise expands the longitude range to Full() so that the rectangle
+ // contains all possible representations of the contained pole(s).
+ S2LatLngRect PolarClosure() const;
+
+ // Returns the smallest rectangle containing the union of this rectangle and
+ // the given rectangle.
+ S2LatLngRect Union(const S2LatLngRect& other) const;
+
+ // Returns the smallest rectangle containing the intersection of this
+ // rectangle and the given rectangle. Note that the region of intersection
+ // may consist of two disjoint rectangles, in which case a single rectangle
+ // spanning both of them is returned.
+ S2LatLngRect Intersection(const S2LatLngRect& other) const;
+
+ // Expands this rectangle so that it contains all points within the given
+ // distance of the boundary, and return the smallest such rectangle. If the
+ // distance is negative, then instead shrinks this rectangle so that it
+ // excludes all points within the given absolute distance of the boundary,
+ // and returns the largest such rectangle.
+ //
+ // Unlike Expanded(), this method treats the rectangle as a set of points on
+ // the sphere, and measures distances on the sphere. For example, you can
+ // use this method to find a rectangle that contains all points within 5km
+ // of a given rectangle. Because this method uses the topology of the
+ // sphere, note the following:
+ //
+ // - The full and empty rectangles have no boundary on the sphere. Any
+ // expansion (positive or negative) of these rectangles leaves them
+ // unchanged.
+ //
+ // - Any rectangle that covers the full longitude range does not have an
+ // east or west boundary, therefore no expansion (positive or negative)
+ // will occur in that direction.
+ //
+ // - Any rectangle that covers the full longitude range and also includes
+ // a pole will not be expanded or contracted at that pole, because it
+ // does not have a boundary there.
+ //
+ // - If a rectangle is within the given distance of a pole, the result will
+ // include the full longitude range (because all longitudes are present
+ // at the poles).
+ //
+ // Expansion and contraction are defined such that they are inverses whenver
+ // possible, i.e.
+ //
+ // rect.ExpandedByDistance(x).ExpandedByDistance(-x) == rect
+ //
+ // (approximately), so long as the first operation does not cause a
+ // rectangle boundary to disappear (i.e., the longitude range newly becomes
+ // full or empty, or the latitude range expands to include a pole).
+ S2LatLngRect ExpandedByDistance(S1Angle distance) const;
+
+ // Returns the minimum distance (measured along the surface of the sphere) to
+ // the given S2LatLngRect. Both S2LatLngRects must be non-empty.
+ S1Angle GetDistance(const S2LatLngRect& other) const;
+
+ // Returns the minimum distance (measured along the surface of the sphere)
+ // from a given point to the rectangle (both its boundary and its interior).
+ // The latlng must be valid.
+ S1Angle GetDistance(const S2LatLng& p) const;
+
+ // Returns the (directed or undirected) Hausdorff distance (measured along the
+ // surface of the sphere) to the given S2LatLngRect. The directed Hausdorff
+ // distance from rectangle A to rectangle B is given by
+ // h(A, B) = max_{p in A} min_{q in B} d(p, q).
+ // The Hausdorff distance between rectangle A and rectangle B is given by
+ // H(A, B) = max{h(A, B), h(B, A)}.
+ S1Angle GetDirectedHausdorffDistance(const S2LatLngRect& other) const;
+ S1Angle GetHausdorffDistance(const S2LatLngRect& other) const;
+
+ // Returns true if two rectangles contains the same set of points.
+ bool operator==(const S2LatLngRect& other) const;
+
+ // Returns the opposite of what operator == returns.
+ bool operator!=(const S2LatLngRect& other) const;
+
+ // Returns true if the latitude and longitude intervals of the two rectangles
+ // are the same up to the given tolerance (see r1interval.h and s1interval.h
+ // for details).
+ bool ApproxEquals(const S2LatLngRect& other,
+ S1Angle max_error = S1Angle::Radians(1e-15)) const;
+
+ // ApproxEquals() with separate tolerances for latitude and longitude.
+ bool ApproxEquals(const S2LatLngRect& other, const S2LatLng& max_error) const;
+
+ ////////////////////////////////////////////////////////////////////////
+ // S2Region interface (see s2region.h for details):
+
+ S2LatLngRect* Clone() const override;
+ S2Cap GetCapBound() const override;
+ S2LatLngRect GetRectBound() const override;
+ bool Contains(const S2Cell& cell) const override;
+
+ // This test is cheap but is NOT exact. Use Intersects() if you want a more
+ // accurate and more expensive test. Note that when this method is used by
+ // an S2RegionCoverer, the accuracy isn't all that important since if a cell
+ // may intersect the region then it is subdivided, and the accuracy of this
+ // method goes up as the cells get smaller.
+ bool MayIntersect(const S2Cell& cell) const override;
+
+ // The point 'p' does not need to be normalized.
+ bool Contains(const S2Point& p) const override;
+
+ // Appends a serialized representation of the S2LatLngRect to "encoder".
+ //
+ // REQUIRES: "encoder" uses the default constructor, so that its buffer
+ // can be enlarged as necessary by calling Ensure(int).
+ void Encode(Encoder* const encoder) const;
+
+ // Decodes an S2LatLngRect encoded with Encode(). Returns true on success.
+ bool Decode(Decoder* const decoder);
+
+ // Returns true if the edge AB intersects the given edge of constant
+ // longitude.
+ static bool IntersectsLngEdge(const S2Point& a, const S2Point& b,
+ const R1Interval& lat, double lng);
+
+ // Returns true if the edge AB intersects the given edge of constant
+ // latitude. Requires the vectors to have unit length.
+ static bool IntersectsLatEdge(const S2Point& a, const S2Point& b,
+ double lat, const S1Interval& lng);
+
+ private:
+ // Helper function. See .cc for description.
+ static S1Angle GetDirectedHausdorffDistance(double lng_diff,
+ const R1Interval& a_lat,
+ const R1Interval& b_lat);
+
+ // Helper function. See .cc for description.
+ static S1Angle GetInteriorMaxDistance(const R1Interval& a_lat,
+ const S2Point& b);
+
+ // Helper function. See .cc for description.
+ static S2Point GetBisectorIntersection(const R1Interval& lat, double lng);
+
+ R1Interval lat_;
+ S1Interval lng_;
+};
+
+inline S2LatLngRect::S2LatLngRect(const S2LatLng& lo, const S2LatLng& hi)
+ : lat_(lo.lat().radians(), hi.lat().radians()),
+ lng_(lo.lng().radians(), hi.lng().radians()) {
+ S2_DLOG_IF(ERROR, !is_valid())
+ << "Invalid rect: " << lo << ", " << hi;
+}
+
+inline S2LatLngRect::S2LatLngRect(const R1Interval& lat, const S1Interval& lng)
+ : lat_(lat), lng_(lng) {
+ S2_DLOG_IF(ERROR, !is_valid())
+ << "Invalid rect: " << lat << ", " << lng;
+}
+
+inline S2LatLngRect::S2LatLngRect()
+ : lat_(R1Interval::Empty()), lng_(S1Interval::Empty()) {
+}
+
+inline S2LatLngRect S2LatLngRect::Empty() {
+ return S2LatLngRect();
+}
+
+inline S2LatLngRect S2LatLngRect::Full() {
+ return S2LatLngRect(FullLat(), FullLng());
+}
+
+inline bool S2LatLngRect::is_valid() const {
+ // The lat/lng ranges must either be both empty or both non-empty.
+ return (std::fabs(lat_.lo()) <= M_PI_2 &&
+ std::fabs(lat_.hi()) <= M_PI_2 &&
+ lng_.is_valid() &&
+ lat_.is_empty() == lng_.is_empty());
+}
+
+inline bool S2LatLngRect::is_empty() const {
+ return lat_.is_empty();
+}
+
+inline bool S2LatLngRect::is_full() const {
+ return lat_ == FullLat() && lng_.is_full();
+}
+
+inline bool S2LatLngRect::is_point() const {
+ return lat_.lo() == lat_.hi() && lng_.lo() == lng_.hi();
+}
+
+inline bool S2LatLngRect::operator==(const S2LatLngRect& other) const {
+ return lat() == other.lat() && lng() == other.lng();
+}
+
+inline bool S2LatLngRect::operator!=(const S2LatLngRect& other) const {
+ return !operator==(other);
+}
+
+std::ostream& operator<<(std::ostream& os, const S2LatLngRect& r);
+
+#endif // S2_S2LATLNG_RECT_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2LATLNG_RECT_BOUNDER_H_
+#define S2_S2LATLNG_RECT_BOUNDER_H_
+
+#include "s2/s2latlng.h"
+#include "s2/s2latlng_rect.h"
+#include "s2/s2point.h"
+
+// This class computes a bounding rectangle that contains all edges defined
+// by a vertex chain v0, v1, v2, ... All vertices must be unit length.
+// Note that the bounding rectangle of an edge can be larger than the
+// bounding rectangle of its endpoints, e.g. consider an edge that passes
+// through the north pole.
+//
+// The bounds are calculated conservatively to account for numerical errors
+// when S2Points are converted to S2LatLngs. More precisely, this class
+// guarantees the following. Let L be a closed edge chain (loop) such that
+// the interior of the loop does not contain either pole. Now if P is any
+// point such that L.Contains(P), then RectBound(L).Contains(S2LatLng(P)).
+class S2LatLngRectBounder {
+ public:
+ S2LatLngRectBounder() : bound_(S2LatLngRect::Empty()) {}
+
+ // This method is called to add a vertex to the chain when the vertex is
+ // represented as an S2Point. Requires that 'b' has unit length. Repeated
+ // vertices are ignored.
+ void AddPoint(const S2Point& b);
+
+ // This method is called to add a vertex to the chain when the vertex is
+ // represented as an S2LatLng. Repeated vertices are ignored.
+ void AddLatLng(const S2LatLng& b_latlng);
+
+ // Returns the bounding rectangle of the edge chain that connects the
+ // vertices defined so far. This bound satisfies the guarantee made
+ // above, i.e. if the edge chain defines a loop, then the bound contains
+ // the S2LatLng coordinates of all S2Points contained by the loop.
+ S2LatLngRect GetBound() const;
+
+ // Expands a bound returned by GetBound() so that it is guaranteed to
+ // contain the bounds of any subregion whose bounds are computed using
+ // this class. For example, consider a loop L that defines a square.
+ // GetBound() ensures that if a point P is contained by this square, then
+ // S2LatLng(P) is contained by the bound. But now consider a diamond
+ // shaped loop S contained by L. It is possible that GetBound() returns a
+ // *larger* bound for S than it does for L, due to rounding errors. This
+ // method expands the bound for L so that it is guaranteed to contain the
+ // bounds of any subregion S.
+ //
+ // More precisely, if L is a loop that does not contain either pole, and S
+ // is a loop such that L.Contains(S), then
+ //
+ // ExpandForSubregions(RectBound(L)).Contains(RectBound(S)).
+ static S2LatLngRect ExpandForSubregions(const S2LatLngRect& bound);
+
+ // Returns the maximum error in GetBound() provided that the result does
+ // not include either pole. It is only to be used for testing purposes
+ // (e.g., by passing it to S2LatLngRect::ApproxEquals).
+ static S2LatLng MaxErrorForTests();
+
+ private:
+ // Common back end for AddPoint() and AddLatLng(). b and b_latlng
+ // must refer to the same vertex.
+ void AddInternal(const S2Point& b, const S2LatLng& b_latlng);
+
+ S2Point a_; // The previous vertex in the chain.
+ S2LatLng a_latlng_; // The corresponding latitude-longitude.
+ S2LatLngRect bound_; // The current bounding rectangle.
+
+ S2LatLngRectBounder(const S2LatLngRectBounder&) = delete;
+ void operator=(const S2LatLngRectBounder&) = delete;
+};
+
+#endif // S2_S2LATLNG_RECT_BOUNDER_H_
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// This file defines various S2Shape types representing loops:
+//
+// S2LaxLoopShape
+// - like S2Loop::Shape but allows duplicate vertices & edges, more compact
+// representation, and faster to initialize.
+//
+// S2LaxClosedPolylineShape
+// - like S2LaxLoopShape, but defines a loop that does not have an interior
+// (a closed polyline).
+//
+// S2VertexIdLaxLoopShape
+// - like S2LaxLoopShape, but vertices are specified as indices into an
+// existing vertex array.
+
+#ifndef S2_S2LAX_LOOP_SHAPE_H_
+#define S2_S2LAX_LOOP_SHAPE_H_
+
+#include <algorithm>
+#include <memory>
+#include <vector>
+
+#include "s2/s2loop.h"
+#include "s2/s2shape.h"
+
+// S2LaxLoopShape represents a closed loop of edges surrounding an interior
+// region. It is similar to S2Loop::Shape except that this class allows
+// duplicate vertices and edges. Loops may have any number of vertices,
+// including 0, 1, or 2. (A one-vertex loop defines a degenerate edge
+// consisting of a single point.)
+//
+// Note that S2LaxLoopShape is faster to initialize and more compact than
+// S2Loop::Shape, but does not support the same operations as S2Loop.
+class S2LaxLoopShape : public S2Shape {
+ public:
+ // Constructs an empty loop.
+ S2LaxLoopShape() : num_vertices_(0) {}
+
+ // Constructs an S2LaxLoopShape with the given vertices.
+ explicit S2LaxLoopShape(const std::vector<S2Point>& vertices);
+
+ // Constructs an S2LaxLoopShape from the given S2Loop, by copying its data.
+ explicit S2LaxLoopShape(const S2Loop& loop);
+
+ // Initializes an S2LaxLoopShape with the given vertices.
+ void Init(const std::vector<S2Point>& vertices);
+
+ // Initializes an S2LaxLoopShape from the given S2Loop, by copying its data.
+ //
+ // REQUIRES: !loop->is_full()
+ // [Use S2LaxPolygonShape if you need to represent a full loop.]
+ void Init(const S2Loop& loop);
+
+ int num_vertices() const { return num_vertices_; }
+ const S2Point& vertex(int i) const { return vertices_[i]; }
+
+ // S2Shape interface:
+ int num_edges() const final { return num_vertices(); }
+ Edge edge(int e) const final;
+ // Not final; overridden by S2LaxClosedPolylineShape.
+ int dimension() const override { return 2; }
+ // Not final; overridden by S2LaxClosedPolylineShape.
+ ReferencePoint GetReferencePoint() const override;
+ int num_chains() const final { return std::min(1, num_vertices_); }
+ Chain chain(int i) const final { return Chain(0, num_vertices_); }
+ Edge chain_edge(int i, int j) const final;
+ ChainPosition chain_position(int e) const final {
+ return ChainPosition(0, e);
+ }
+
+ private:
+ // For clients that have many small loops, we save some memory by
+ // representing the vertices as an array rather than using std::vector.
+ int32 num_vertices_;
+ std::unique_ptr<S2Point[]> vertices_;
+};
+
+// S2LaxClosedPolylineShape is like S2LaxPolylineShape except that the last
+// vertex is implicitly joined to the first. It is also like S2LaxLoopShape
+// except that it does not have an interior (which makes it more efficient to
+// index).
+class S2LaxClosedPolylineShape : public S2LaxLoopShape {
+ public:
+ // See S2LaxLoopShape for constructors.
+ using S2LaxLoopShape::S2LaxLoopShape;
+
+ int dimension() const final { return 1; }
+ ReferencePoint GetReferencePoint() const final {
+ return ReferencePoint::Contained(false);
+ }
+};
+
+// S2VertexIdLaxLoopShape is just like S2LaxLoopShape, except that vertices are
+// specified as indices into a vertex array. This representation can be more
+// compact when many loops are arranged in a mesh structure.
+class S2VertexIdLaxLoopShape : public S2Shape {
+ public:
+ // Constructs an empty loop.
+ S2VertexIdLaxLoopShape() : num_vertices_(0) {}
+
+ // Constructs the shape from the given vertex array and indices.
+ // "vertex_ids" is a vector of indices into "vertex_array".
+ //
+ // ENSURES: loop->vertex(i) == (*vertex_array)[vertex_ids[i]]
+ // REQUIRES: "vertex_array" persists for the lifetime of this object.
+ explicit S2VertexIdLaxLoopShape(const std::vector<int32>& vertex_ids,
+ const S2Point* vertex_array);
+
+ // Initializes the shape from the given vertex array and indices.
+ // "vertex_ids" is a vector of indices into "vertex_array".
+ void Init(const std::vector<int32>& vertex_ids,
+ const S2Point* vertex_array);
+
+ // Returns the number of vertices in the loop.
+ int num_vertices() const { return num_vertices_; }
+ int32 vertex_id(int i) const { return vertex_ids_[i]; }
+ const S2Point& vertex(int i) const { return vertex_array_[vertex_id(i)]; }
+
+ // S2Shape interface:
+ int num_edges() const final { return num_vertices(); }
+ Edge edge(int e) const final;
+ int dimension() const final { return 2; }
+ ReferencePoint GetReferencePoint() const final;
+ int num_chains() const final { return std::min(1, num_vertices_); }
+ Chain chain(int i) const final { return Chain(0, num_vertices_); }
+ Edge chain_edge(int i, int j) const final;
+ ChainPosition chain_position(int e) const final {
+ return ChainPosition(0, e);
+ }
+
+ private:
+ int32 num_vertices_;
+ std::unique_ptr<int32[]> vertex_ids_;
+ const S2Point* vertex_array_;
+};
+
+#endif // S2_S2LAX_LOOP_SHAPE_H_
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2LAX_POLYGON_SHAPE_H_
+#define S2_S2LAX_POLYGON_SHAPE_H_
+
+#include <memory>
+#include <vector>
+
+#include "s2/third_party/absl/types/span.h"
+#include "s2/encoded_s2point_vector.h"
+#include "s2/encoded_uint_vector.h"
+#include "s2/s2polygon.h"
+#include "s2/s2shape.h"
+
+// S2LaxPolygonShape represents a region defined by a collection of zero or
+// more closed loops. The interior is the region to the left of all loops.
+// This is similar to S2Polygon::Shape except that this class supports
+// polygons with degeneracies. Degeneracies are of two types: degenerate
+// edges (from a vertex to itself) and sibling edge pairs (consisting of two
+// oppositely oriented edges). Degeneracies can represent either "shells" or
+// "holes" depending on the loop they are contained by. For example, a
+// degenerate edge or sibling pair contained by a "shell" would be interpreted
+// as a degenerate hole. Such edges form part of the boundary of the polygon.
+//
+// Loops with fewer than three vertices are interpreted as follows:
+// - A loop with two vertices defines two edges (in opposite directions).
+// - A loop with one vertex defines a single degenerate edge.
+// - A loop with no vertices is interpreted as the "full loop" containing
+// all points on the sphere. If this loop is present, then all other loops
+// must form degeneracies (i.e., degenerate edges or sibling pairs). For
+// example, two loops {} and {X} would be interpreted as the full polygon
+// with a degenerate single-point hole at X.
+//
+// S2LaxPolygonShape does not have any error checking, and it is perfectly
+// fine to create S2LaxPolygonShape objects that do not meet the requirements
+// below (e.g., in order to analyze or fix those problems). However,
+// S2LaxPolygonShapes must satisfy some additional conditions in order to
+// perform certain operations:
+//
+// - In order to be valid for point containment tests, the polygon must
+// satisfy the "interior is on the left" rule. This means that there must
+// not be any crossing edges, and if there are duplicate edges then all but
+// at most one of thm must belong to a sibling pair (i.e., the number of
+// edges in opposite directions must differ by at most one).
+//
+// - To be valid for boolean operations (S2BooleanOperation), degenerate
+// edges and sibling pairs cannot coincide with any other edges. For
+// example, the following situations are not allowed:
+//
+// {AA, AA} // degenerate edge coincides with another edge
+// {AA, AB} // degenerate edge coincides with another edge
+// {AB, BA, AB} // sibling pair coincides with another edge
+//
+// Note that S2LaxPolygonShape is must faster to initialize and is more
+// compact than S2Polygon, but unlike S2Polygon it does not have any built-in
+// operations. Instead you should use S2ShapeIndex operations
+// (S2BooleanOperation, S2ClosestEdgeQuery, etc).
+class S2LaxPolygonShape : public S2Shape {
+ public:
+ static constexpr TypeTag kTypeTag = 5;
+
+ // Constructs an empty polygon.
+ S2LaxPolygonShape() : num_loops_(0), num_vertices_(0) {}
+
+ // Constructs an S2LaxPolygonShape from the given vertex loops.
+ using Loop = std::vector<S2Point>;
+ explicit S2LaxPolygonShape(const std::vector<Loop>& loops);
+
+ // Constructs an S2LaxPolygonShape from an S2Polygon, by copying its data.
+ // Full and empty S2Polygons are supported.
+ explicit S2LaxPolygonShape(const S2Polygon& polygon);
+
+ ~S2LaxPolygonShape() override;
+
+ // Initializes an S2LaxPolygonShape from the given vertex loops.
+ void Init(const std::vector<Loop>& loops);
+
+ // Initializes an S2LaxPolygonShape from an S2Polygon, by copying its data.
+ // Full and empty S2Polygons are supported.
+ void Init(const S2Polygon& polygon);
+
+ // Returns the number of loops.
+ int num_loops() const { return num_loops_; }
+
+ // Returns the total number of vertices in all loops.
+ int num_vertices() const;
+
+ // Returns the number of vertices in the given loop.
+ int num_loop_vertices(int i) const;
+
+ // Returns the vertex from loop "i" at index "j".
+ // REQUIRES: 0 <= i < num_loops()
+ // REQUIRES: 0 <= j < num_loop_vertices(i)
+ const S2Point& loop_vertex(int i, int j) const;
+
+ // Appends an encoded representation of the S2LaxPolygonShape to "encoder".
+ //
+ // REQUIRES: "encoder" uses the default constructor, so that its buffer
+ // can be enlarged as necessary by calling Ensure(int).
+ void Encode(Encoder* encoder,
+ s2coding::CodingHint hint = s2coding::CodingHint::COMPACT) const;
+
+ // Decodes an S2LaxPolygonShape, returning true on success. (The method
+ // name is chosen for compatibility with EncodedS2LaxPolygonShape below.)
+ bool Init(Decoder* decoder);
+
+ // S2Shape interface:
+ int num_edges() const final { return num_vertices(); }
+ Edge edge(int e) const final;
+ int dimension() const final { return 2; }
+ ReferencePoint GetReferencePoint() const final;
+ int num_chains() const final { return num_loops(); }
+ Chain chain(int i) const final;
+ Edge chain_edge(int i, int j) const final;
+ ChainPosition chain_position(int e) const final;
+ TypeTag type_tag() const override { return kTypeTag; }
+
+ private:
+ void Init(const std::vector<absl::Span<const S2Point>>& loops);
+
+ int32 num_loops_;
+ std::unique_ptr<S2Point[]> vertices_;
+ // If num_loops_ <= 1, this union stores the number of vertices.
+ // Otherwise it points to an array of size (num_loops + 1) where element "i"
+ // is the total number of vertices in loops 0..i-1.
+ union {
+ int32 num_vertices_;
+ uint32* cumulative_vertices_; // Don't use unique_ptr in unions.
+ };
+};
+
+// Exactly like S2LaxPolygonShape, except that the vertices are kept in an
+// encoded form and are decoded only as they are accessed. This allows for
+// very fast initialization and no additional memory use beyond the encoded
+// data. The encoded data is not owned by this class; typically it points
+// into a large contiguous buffer that contains other encoded data as well.
+class EncodedS2LaxPolygonShape : public S2Shape {
+ public:
+ // Constructs an uninitialized object; requires Init() to be called.
+ EncodedS2LaxPolygonShape() {}
+
+ // Initializes an EncodedS2LaxPolygonShape.
+ //
+ // REQUIRES: The Decoder data buffer must outlive this object.
+ bool Init(Decoder* decoder);
+
+ int num_loops() const { return num_loops_; }
+ int num_vertices() const;
+ int num_loop_vertices(int i) const;
+ S2Point loop_vertex(int i, int j) const;
+
+ // S2Shape interface:
+ int num_edges() const final { return num_vertices(); }
+ Edge edge(int e) const final;
+ int dimension() const final { return 2; }
+ ReferencePoint GetReferencePoint() const final;
+ int num_chains() const final { return num_loops(); }
+ Chain chain(int i) const final;
+ Edge chain_edge(int i, int j) const final;
+ ChainPosition chain_position(int e) const final;
+
+ private:
+ int32 num_loops_;
+ s2coding::EncodedS2PointVector vertices_;
+ s2coding::EncodedUintVector<uint32> cumulative_vertices_;
+};
+
+#endif // S2_S2LAX_POLYGON_SHAPE_H_
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2LAX_POLYLINE_SHAPE_H_
+#define S2_S2LAX_POLYLINE_SHAPE_H_
+
+#include <memory>
+#include <vector>
+#include "s2/encoded_s2point_vector.h"
+#include "s2/s2polyline.h"
+#include "s2/s2shape.h"
+
+// S2LaxPolylineShape represents a polyline. It is similar to
+// S2Polyline::Shape except that duplicate vertices are allowed, and the
+// representation is slightly more compact.
+//
+// Polylines may have any number of vertices, but note that polylines with
+// fewer than 2 vertices do not define any edges. (To create a polyline
+// consisting of a single degenerate edge, either repeat the same vertex twice
+// or use S2LaxClosedPolylineShape defined in s2_lax_loop_shape.h.)
+class S2LaxPolylineShape : public S2Shape {
+ public:
+ static constexpr TypeTag kTypeTag = 4;
+
+ // Constructs an empty polyline.
+ S2LaxPolylineShape() : num_vertices_(0) {}
+
+ // Constructs an S2LaxPolylineShape with the given vertices.
+ explicit S2LaxPolylineShape(const std::vector<S2Point>& vertices);
+
+ // Constructs an S2LaxPolylineShape from the given S2Polyline, by copying
+ // its data.
+ explicit S2LaxPolylineShape(const S2Polyline& polyline);
+
+ // Initializes an S2LaxPolylineShape with the given vertices.
+ void Init(const std::vector<S2Point>& vertices);
+
+ // Initializes an S2LaxPolylineShape from the given S2Polyline, by copying
+ // its data.
+ void Init(const S2Polyline& polyline);
+
+ int num_vertices() const { return num_vertices_; }
+ const S2Point& vertex(int i) const { return vertices_[i]; }
+
+ // Appends an encoded representation of the S2LaxPolylineShape to "encoder".
+ //
+ // REQUIRES: "encoder" uses the default constructor, so that its buffer
+ // can be enlarged as necessary by calling Ensure(int).
+ void Encode(Encoder* encoder,
+ s2coding::CodingHint hint = s2coding::CodingHint::COMPACT) const;
+
+ // Decodes an S2LaxPolylineShape, returning true on success. (The method
+ // name is chosen for compatibility with EncodedS2LaxPolylineShape below.)
+ bool Init(Decoder* decoder);
+
+ // S2Shape interface:
+ int num_edges() const final { return std::max(0, num_vertices() - 1); }
+ Edge edge(int e) const final;
+ int dimension() const final { return 1; }
+ ReferencePoint GetReferencePoint() const final {
+ return ReferencePoint::Contained(false);
+ }
+ int num_chains() const final;
+ Chain chain(int i) const final;
+ Edge chain_edge(int i, int j) const final;
+ ChainPosition chain_position(int e) const final;
+ TypeTag type_tag() const override { return kTypeTag; }
+
+ private:
+ // For clients that have many small polylines, we save some memory by
+ // representing the vertices as an array rather than using std::vector.
+ int32 num_vertices_;
+ std::unique_ptr<S2Point[]> vertices_;
+};
+
+// Exactly like S2LaxPolylineShape, except that the vertices are kept in an
+// encoded form and are decoded only as they are accessed. This allows for
+// very fast initialization and no additional memory use beyond the encoded
+// data. The encoded data is not owned by this class; typically it points
+// into a large contiguous buffer that contains other encoded data as well.
+class EncodedS2LaxPolylineShape : public S2Shape {
+ public:
+ // Constructs an uninitialized object; requires Init() to be called.
+ EncodedS2LaxPolylineShape() {}
+
+ // Initializes an EncodedS2LaxPolylineShape.
+ //
+ // REQUIRES: The Decoder data buffer must outlive this object.
+ bool Init(Decoder* decoder);
+
+ int num_vertices() const { return vertices_.size(); }
+ S2Point vertex(int i) const { return vertices_[i]; }
+
+ // S2Shape interface:
+ int num_edges() const final { return std::max(0, num_vertices() - 1); }
+ Edge edge(int e) const final;
+ int dimension() const final { return 1; }
+ ReferencePoint GetReferencePoint() const final {
+ return ReferencePoint::Contained(false);
+ }
+ int num_chains() const final;
+ Chain chain(int i) const final;
+ Edge chain_edge(int i, int j) const final;
+ ChainPosition chain_position(int e) const final;
+
+ private:
+ s2coding::EncodedS2PointVector vertices_;
+};
+
+#endif // S2_S2LAX_POLYLINE_SHAPE_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2LOOP_H_
+#define S2_S2LOOP_H_
+
+#include <atomic>
+#include <bitset>
+#include <cmath>
+#include <cstddef>
+#include <map>
+#include <vector>
+
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/mutable_s2shape_index.h"
+#include "s2/s1angle.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s2debug.h"
+#include "s2/s2latlng_rect.h"
+#include "s2/s2loop_measures.h"
+#include "s2/s2pointutil.h"
+#include "s2/s2region.h"
+#include "s2/s2shape_index.h"
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/util/math/matrix3x3.h"
+#include "s2/util/math/vector.h"
+
+class Decoder;
+class Encoder;
+class LoopCrosser;
+class LoopRelation;
+class MergingIterator;
+class S2Cap;
+class S2Cell;
+class S2CrossingEdgeQuery;
+class S2Error;
+class S2Loop;
+struct S2XYZFaceSiTi;
+namespace s2builderutil { class S2PolygonLayer; }
+
+// An S2Loop represents a simple spherical polygon. It consists of a single
+// chain of vertices where the first vertex is implicitly connected to the
+// last. All loops are defined to have a CCW orientation, i.e. the interior of
+// the loop is on the left side of the edges. This implies that a clockwise
+// loop enclosing a small area is interpreted to be a CCW loop enclosing a
+// very large area.
+//
+// Loops are not allowed to have any duplicate vertices (whether adjacent or
+// not). Non-adjacent edges are not allowed to intersect, and furthermore edges
+// of length 180 degrees are not allowed (i.e., adjacent vertices cannot be
+// antipodal). Loops must have at least 3 vertices (except for the empty and
+// full loops discussed below). Although these restrictions are not enforced
+// in optimized code, you may get unexpected results if they are violated.
+//
+// There are two special loops: the "empty loop" contains no points, while the
+// "full loop" contains all points. These loops do not have any edges, but to
+// preserve the invariant that every loop can be represented as a vertex
+// chain, they are defined as having exactly one vertex each (see kEmpty and
+// kFull).
+//
+// Point containment of loops is defined such that if the sphere is subdivided
+// into faces (loops), every point is contained by exactly one face. This
+// implies that loops do not necessarily contain their vertices.
+//
+// Note: The reason that duplicate vertices and intersecting edges are not
+// allowed is that they make it harder to define and implement loop
+// relationships, e.g. whether one loop contains another. If your data does
+// not satisfy these restrictions, you can use S2Builder to normalize it.
+class S2Loop final : public S2Region {
+ public:
+ // Default constructor. The loop must be initialized by calling Init() or
+ // Decode() before it is used.
+ S2Loop();
+
+ // Convenience constructor that calls Init() with the given vertices.
+ explicit S2Loop(const std::vector<S2Point>& vertices);
+
+ // Convenience constructor to disable the automatic validity checking
+ // controlled by the --s2debug flag. Example:
+ //
+ // S2Loop* loop = new S2Loop(vertices, S2Debug::DISABLE);
+ //
+ // This is equivalent to:
+ //
+ // S2Loop* loop = new S2Loop;
+ // loop->set_s2debug_override(S2Debug::DISABLE);
+ // loop->Init(vertices);
+ //
+ // The main reason to use this constructor is if you intend to call
+ // IsValid() explicitly. See set_s2debug_override() for details.
+ S2Loop(const std::vector<S2Point>& vertices, S2Debug override);
+
+ // Initialize a loop with given vertices. The last vertex is implicitly
+ // connected to the first. All points should be unit length. Loops must
+ // have at least 3 vertices (except for the empty and full loops, see
+ // kEmpty and kFull). This method may be called multiple times.
+ void Init(const std::vector<S2Point>& vertices);
+
+ // A special vertex chain of length 1 that creates an empty loop (i.e., a
+ // loop with no edges that contains no points). Example usage:
+ //
+ // S2Loop empty(S2Loop::kEmpty());
+ //
+ // The loop may be safely encoded lossily (e.g. by snapping it to an S2Cell
+ // center) as long as its position does not move by 90 degrees or more.
+ static std::vector<S2Point> kEmpty();
+
+ // A special vertex chain of length 1 that creates a full loop (i.e., a loop
+ // with no edges that contains all points). See kEmpty() for details.
+ static std::vector<S2Point> kFull();
+
+ // Construct a loop corresponding to the given cell.
+ //
+ // Note that the loop and cell *do not* contain exactly the same set of
+ // points, because S2Loop and S2Cell have slightly different definitions of
+ // point containment. For example, an S2Cell vertex is contained by all
+ // four neighboring S2Cells, but it is contained by exactly one of four
+ // S2Loops constructed from those cells. As another example, the S2Cell
+ // coverings of "cell" and "S2Loop(cell)" will be different, because the
+ // loop contains points on its boundary that actually belong to other cells
+ // (i.e., the covering will include a layer of neighboring cells).
+ explicit S2Loop(const S2Cell& cell);
+
+ ~S2Loop() override;
+
+ // Allows overriding the automatic validity checks controlled by the
+ // --s2debug flag. If this flag is true, then loops are automatically
+ // checked for validity as they are initialized. The main reason to disable
+ // this flag is if you intend to call IsValid() explicitly, like this:
+ //
+ // S2Loop loop;
+ // loop.set_s2debug_override(S2Debug::DISABLE);
+ // loop.Init(...);
+ // if (!loop.IsValid()) { ... }
+ //
+ // Without the call to set_s2debug_override(), invalid data would cause a
+ // fatal error in Init() whenever the --s2debug flag is enabled.
+ //
+ // This setting is preserved across calls to Init() and Decode().
+ void set_s2debug_override(S2Debug override);
+ S2Debug s2debug_override() const;
+
+ // Returns true if this is a valid loop. Note that validity is checked
+ // automatically during initialization when --s2debug is enabled (true by
+ // default in debug binaries).
+ bool IsValid() const;
+
+ // Returns true if this is *not* a valid loop and sets "error"
+ // appropriately. Otherwise returns false and leaves "error" unchanged.
+ //
+ // REQUIRES: error != nullptr
+ bool FindValidationError(S2Error* error) const;
+
+ int num_vertices() const { return num_vertices_; }
+
+ // For convenience, we make two entire copies of the vertex list available:
+ // vertex(n..2*n-1) is mapped to vertex(0..n-1), where n == num_vertices().
+ //
+ // REQUIRES: 0 <= i < 2 * num_vertices()
+ const S2Point& vertex(int i) const {
+ S2_DCHECK_GE(i, 0);
+ S2_DCHECK_LT(i, 2 * num_vertices());
+ int j = i - num_vertices();
+ return vertices_[j < 0 ? i : j];
+ }
+
+ // Like vertex(), but this method returns vertices in reverse order if the
+ // loop represents a polygon hole. For example, arguments 0, 1, 2 are
+ // mapped to vertices n-1, n-2, n-3, where n == num_vertices(). This
+ // ensures that the interior of the polygon is always to the left of the
+ // vertex chain.
+ //
+ // REQUIRES: 0 <= i < 2 * num_vertices()
+ const S2Point& oriented_vertex(int i) const {
+ S2_DCHECK_GE(i, 0);
+ S2_DCHECK_LT(i, 2 * num_vertices());
+ int j = i - num_vertices();
+ if (j < 0) j = i;
+ if (is_hole()) j = num_vertices() - 1 - j;
+ return vertices_[j];
+ }
+
+ // Returns true if this is the special empty loop that contains no points.
+ bool is_empty() const;
+
+ // Returns true if this is the special full loop that contains all points.
+ bool is_full() const;
+
+ // Returns true if this loop is either empty or full.
+ bool is_empty_or_full() const;
+
+ // The depth of a loop is defined as its nesting level within its containing
+ // polygon. "Outer shell" loops have depth 0, holes within those loops have
+ // depth 1, shells within those holes have depth 2, etc. This field is only
+ // used by the S2Polygon implementation.
+ int depth() const { return depth_; }
+ void set_depth(int depth) { depth_ = depth; }
+
+ // Returns true if this loop represents a hole in its containing polygon.
+ bool is_hole() const { return (depth_ & 1) != 0; }
+
+ // The sign of a loop is -1 if the loop represents a hole in its containing
+ // polygon, and +1 otherwise.
+ int sign() const { return is_hole() ? -1 : 1; }
+
+ // Returns true if the loop area is at most 2*Pi. Degenerate loops are
+ // handled consistently with s2pred::Sign(), i.e., if a loop can be
+ // expressed as the union of degenerate or nearly-degenerate CCW triangles,
+ // then it will always be considered normalized.
+ bool IsNormalized() const;
+
+ // Invert the loop if necessary so that the area enclosed by the loop is at
+ // most 2*Pi.
+ void Normalize();
+
+ // Reverse the order of the loop vertices, effectively complementing the
+ // region represented by the loop. For example, the loop ABCD (with edges
+ // AB, BC, CD, DA) becomes the loop DCBA (with edges DC, CB, BA, AD).
+ // Notice that the last edge is the same in both cases except that its
+ // direction has been reversed.
+ void Invert();
+
+ // Returns the area of the loop interior, i.e. the region on the left side of
+ // the loop. The return value is between 0 and 4*Pi. (Note that the return
+ // value is not affected by whether this loop is a "hole" or a "shell".)
+ double GetArea() const;
+
+ // Returns the true centroid of the loop multiplied by the area of the loop
+ // (see s2centroids.h for details on centroids). The result is not unit
+ // length, so you may want to normalize it. Also note that in general, the
+ // centroid may not be contained by the loop.
+ //
+ // We prescale by the loop area for two reasons: (1) it is cheaper to
+ // compute this way, and (2) it makes it easier to compute the centroid of
+ // more complicated shapes (by splitting them into disjoint regions and
+ // adding their centroids).
+ //
+ // Note that the return value is not affected by whether this loop is a
+ // "hole" or a "shell".
+ S2Point GetCentroid() const;
+
+ // Returns the geodesic curvature of the loop, defined as the sum of the turn
+ // angles at each vertex (see S2::TurnAngle). The result is positive if the
+ // loop is counter-clockwise, negative if the loop is clockwise, and zero if
+ // the loop is a great circle. The geodesic curvature is equal to 2*Pi minus
+ // the area of the loop.
+ //
+ // Degenerate and nearly-degenerate loops are handled consistently with
+ // s2pred::Sign(). So for example, if a loop has zero area (i.e., it is a
+ // very small CCW loop) then its geodesic curvature will always be positive.
+ double GetCurvature() const;
+
+ // Returns the maximum error in GetCurvature(). The return value is not
+ // constant; it depends on the loop.
+ double GetCurvatureMaxError() const;
+
+ // Returns the distance from the given point to the loop interior. If the
+ // loop is empty, return S1Angle::Infinity(). "x" should be unit length.
+ S1Angle GetDistance(const S2Point& x) const;
+
+ // Returns the distance from the given point to the loop boundary. If the
+ // loop is empty or full, return S1Angle::Infinity() (since the loop has no
+ // boundary). "x" should be unit length.
+ S1Angle GetDistanceToBoundary(const S2Point& x) const;
+
+ // If the given point is contained by the loop, return it. Otherwise return
+ // the closest point on the loop boundary. If the loop is empty, return the
+ // input argument. Note that the result may or may not be contained by the
+ // loop. "x" should be unit length.
+ S2Point Project(const S2Point& x) const;
+
+ // Returns the closest point on the loop boundary to the given point. If the
+ // loop is empty or full, return the input argument (since the loop has no
+ // boundary). "x" should be unit length.
+ S2Point ProjectToBoundary(const S2Point& x) const;
+
+ // Returns true if the region contained by this loop is a superset of the
+ // region contained by the given other loop.
+ bool Contains(const S2Loop* b) const;
+
+ // Returns true if the region contained by this loop intersects the region
+ // contained by the given other loop.
+ bool Intersects(const S2Loop* b) const;
+
+ // Returns true if two loops have the same vertices in the same linear order
+ // (i.e., cyclic rotations are not allowed).
+ bool Equals(const S2Loop* b) const;
+
+ // Returns true if two loops have the same boundary. This is true if and
+ // only if the loops have the same vertices in the same cyclic order (i.e.,
+ // the vertices may be cyclically rotated). The empty and full loops are
+ // considered to have different boundaries.
+ bool BoundaryEquals(const S2Loop* b) const;
+
+ // Returns true if two loops have the same boundary except for vertex
+ // perturbations. More precisely, the vertices in the two loops must be in
+ // the same cyclic order, and corresponding vertex pairs must be separated
+ // by no more than "max_error".
+ bool BoundaryApproxEquals(const S2Loop& b,
+ S1Angle max_error = S1Angle::Radians(1e-15)) const;
+
+ // Returns true if the two loop boundaries are within "max_error" of each
+ // other along their entire lengths. The two loops may have different
+ // numbers of vertices. More precisely, this method returns true if the two
+ // loops have parameterizations a:[0,1] -> S^2, b:[0,1] -> S^2 such that
+ // distance(a(t), b(t)) <= max_error for all t. You can think of this as
+ // testing whether it is possible to drive two cars all the way around the
+ // two loops such that no car ever goes backward and the cars are always
+ // within "max_error" of each other.
+ bool BoundaryNear(const S2Loop& b,
+ S1Angle max_error = S1Angle::Radians(1e-15)) const;
+
+ // This method computes the oriented surface integral of some quantity f(x)
+ // over the loop interior, given a function f_tri(A,B,C) that returns the
+ // corresponding integral over the spherical triangle ABC. Here "oriented
+ // surface integral" means:
+ //
+ // (1) f_tri(A,B,C) must be the integral of f if ABC is counterclockwise,
+ // and the integral of -f if ABC is clockwise.
+ //
+ // (2) The result of this function is *either* the integral of f over the
+ // loop interior, or the integral of (-f) over the loop exterior.
+ //
+ // Note that there are at least two common situations where it easy to work
+ // around property (2) above:
+ //
+ // - If the integral of f over the entire sphere is zero, then it doesn't
+ // matter which case is returned because they are always equal.
+ //
+ // - If f is non-negative, then it is easy to detect when the integral over
+ // the loop exterior has been returned, and the integral over the loop
+ // interior can be obtained by adding the integral of f over the entire
+ // unit sphere (a constant) to the result.
+ //
+ // Also requires that the default constructor for T must initialize the
+ // value to zero. (This is true for built-in types such as "double".)
+ template <class T>
+ T GetSurfaceIntegral(T f_tri(const S2Point&, const S2Point&, const S2Point&))
+ const;
+
+ // Constructs a regular polygon with the given number of vertices, all
+ // located on a circle of the specified radius around "center". The radius
+ // is the actual distance from "center" to each vertex.
+ static std::unique_ptr<S2Loop> MakeRegularLoop(const S2Point& center,
+ S1Angle radius,
+ int num_vertices);
+
+ // Like the function above, but this version constructs a loop centered
+ // around the z-axis of the given coordinate frame, with the first vertex in
+ // the direction of the positive x-axis. (This allows the loop to be
+ // rotated for testing purposes.)
+ static std::unique_ptr<S2Loop> MakeRegularLoop(const Matrix3x3_d& frame,
+ S1Angle radius,
+ int num_vertices);
+
+ // Returnss the total number of bytes used by the loop.
+ size_t SpaceUsed() const;
+
+ ////////////////////////////////////////////////////////////////////////
+ // S2Region interface (see s2region.h for details):
+
+ S2Loop* Clone() const override;
+
+ // GetRectBound() returns essentially tight results, while GetCapBound()
+ // might have a lot of extra padding. Both bounds are conservative in that
+ // if the loop contains a point P, then the bound contains P also.
+ S2Cap GetCapBound() const override;
+ S2LatLngRect GetRectBound() const override { return bound_; }
+
+ bool Contains(const S2Cell& cell) const override;
+ bool MayIntersect(const S2Cell& cell) const override;
+
+ // The point 'p' does not need to be normalized.
+ bool Contains(const S2Point& p) const override;
+
+ // Appends a serialized representation of the S2Loop to "encoder".
+ //
+ // Generally clients should not use S2Loop::Encode(). Instead they should
+ // encode an S2Polygon, which unlike this method supports (lossless)
+ // compression.
+ //
+ // REQUIRES: "encoder" uses the default constructor, so that its buffer
+ // can be enlarged as necessary by calling Ensure(int).
+ void Encode(Encoder* const encoder) const;
+
+ // Decodes a loop encoded with Encode() or the private method
+ // EncodeCompressed() (used by the S2Polygon encoder). Returns true on
+ // success.
+ //
+ // This method may be called with loops that have already been initialized.
+ bool Decode(Decoder* const decoder);
+
+ // Provides the same functionality as Decode, except that decoded regions
+ // are allowed to point directly into the Decoder's memory buffer rather
+ // than copying the data. This can be much faster, but the decoded loop is
+ // only valid within the scope (lifetime) of the Decoder's memory buffer.
+ bool DecodeWithinScope(Decoder* const decoder);
+
+ ////////////////////////////////////////////////////////////////////////
+ // Methods intended primarily for use by the S2Polygon implementation:
+
+ // Given two loops of a polygon, return true if A contains B. This version
+ // of Contains() is cheap because it does not test for edge intersections.
+ // The loops must meet all the S2Polygon requirements; for example this
+ // implies that their boundaries may not cross or have any shared edges
+ // (although they may have shared vertices).
+ bool ContainsNested(const S2Loop* b) const;
+
+ // Returns +1 if A contains the boundary of B, -1 if A excludes the boundary
+ // of B, and 0 if the boundaries of A and B cross. Shared edges are handled
+ // as follows: If XY is a shared edge, define Reversed(XY) to be true if XY
+ // appears in opposite directions in A and B. Then A contains XY if and
+ // only if Reversed(XY) == B->is_hole(). (Intuitively, this checks whether
+ // A contains a vanishingly small region extending from the boundary of B
+ // toward the interior of the polygon to which loop B belongs.)
+ //
+ // This method is used for testing containment and intersection of
+ // multi-loop polygons. Note that this method is not symmetric, since the
+ // result depends on the direction of loop A but not on the direction of
+ // loop B (in the absence of shared edges).
+ //
+ // REQUIRES: neither loop is empty.
+ // REQUIRES: if b->is_full(), then !b->is_hole().
+ int CompareBoundary(const S2Loop* b) const;
+
+ // Given two loops whose boundaries do not cross (see CompareBoundary),
+ // return true if A contains the boundary of B. If "reverse_b" is true, the
+ // boundary of B is reversed first (which only affects the result when there
+ // are shared edges). This method is cheaper than CompareBoundary() because
+ // it does not test for edge intersections.
+ //
+ // REQUIRES: neither loop is empty.
+ // REQUIRES: if b->is_full(), then reverse_b == false.
+ bool ContainsNonCrossingBoundary(const S2Loop* b, bool reverse_b) const;
+
+ // Wrapper class for indexing a loop (see S2ShapeIndex). Once this object
+ // is inserted into an S2ShapeIndex it is owned by that index, and will be
+ // automatically deleted when no longer needed by the index. Note that this
+ // class does not take ownership of the loop itself (see OwningShape below).
+ // You can also subtype this class to store additional data (see S2Shape for
+ // details).
+#ifndef SWIG
+ class Shape : public S2Shape {
+ public:
+ Shape() : loop_(nullptr) {} // Must call Init().
+
+ // Initialize the shape. Does not take ownership of "loop".
+ explicit Shape(const S2Loop* loop) { Init(loop); }
+ void Init(const S2Loop* loop) { loop_ = loop; }
+
+ const S2Loop* loop() const { return loop_; }
+
+ // S2Shape interface:
+ int num_edges() const final {
+ return loop_->is_empty_or_full() ? 0 : loop_->num_vertices();
+ }
+ Edge edge(int e) const final {
+ return Edge(loop_->vertex(e), loop_->vertex(e + 1));
+ }
+ int dimension() const final { return 2; }
+ ReferencePoint GetReferencePoint() const final {
+ return ReferencePoint(S2::Origin(), loop_->contains_origin());
+ }
+ int num_chains() const final;
+ Chain chain(int i) const final;
+ Edge chain_edge(int i, int j) const final {
+ S2_DCHECK_EQ(i, 0);
+ return Edge(loop_->vertex(j), loop_->vertex(j + 1));
+ }
+ ChainPosition chain_position(int e) const final {
+ return ChainPosition(0, e);
+ }
+
+ private:
+ const S2Loop* loop_;
+ };
+
+ // Like Shape, except that the S2Loop is automatically deleted when this
+ // object is deleted by the S2ShapeIndex. This is useful when an S2Loop
+ // is constructed solely for the purpose of indexing it.
+ class OwningShape : public Shape {
+ public:
+ OwningShape() {} // Must call Init().
+ explicit OwningShape(std::unique_ptr<const S2Loop> loop)
+ : Shape(loop.release()) {
+ }
+ void Init(std::unique_ptr<const S2Loop> loop) {
+ Shape::Init(loop.release());
+ }
+ ~OwningShape() override { delete loop(); }
+ };
+#endif // SWIG
+
+ private:
+ // All of the following need access to contains_origin(). Possibly this
+ // method should be public.
+ friend class Shape;
+ friend class S2Polygon;
+ friend class S2Stats;
+ friend class S2LoopTestBase;
+ friend class LoopCrosser;
+ friend class s2builderutil::S2PolygonLayer;
+
+ // Internal copy constructor used only by Clone() that makes a deep copy of
+ // its argument.
+ S2Loop(const S2Loop& src);
+
+ // Returns an S2PointLoopSpan containing the loop vertices, for use with the
+ // functions defined in s2loop_measures.h.
+ S2PointLoopSpan vertices_span() const {
+ return S2PointLoopSpan(vertices_, num_vertices());
+ }
+
+ // Returns true if this loop contains S2::Origin().
+ bool contains_origin() const { return origin_inside_; }
+
+ // The single vertex in the "empty loop" vertex chain.
+ static S2Point kEmptyVertex();
+
+ // The single vertex in the "full loop" vertex chain.
+ static S2Point kFullVertex();
+
+ void InitOriginAndBound();
+ void InitBound();
+ void InitIndex();
+
+ // A version of Contains(S2Point) that does not use the S2ShapeIndex.
+ // Used by the S2Polygon implementation.
+ bool BruteForceContains(const S2Point& p) const;
+
+ // Like FindValidationError(), but skips any checks that would require
+ // building the S2ShapeIndex (i.e., self-intersection tests). This is used
+ // by the S2Polygon implementation, which uses its own index to check for
+ // loop self-intersections.
+ bool FindValidationErrorNoIndex(S2Error* error) const;
+
+ // Internal implementation of the Decode and DecodeWithinScope methods above.
+ // If within_scope is true, memory is allocated for vertices_ and data
+ // is copied from the decoder using std::copy. If it is false, vertices_
+ // will point to the memory area inside the decoder, and the field
+ // owns_vertices_ is set to false.
+ bool DecodeInternal(Decoder* const decoder, bool within_scope);
+
+ // Converts the loop vertices to the S2XYZFaceSiTi format and store the result
+ // in the given array, which must be large enough to store all the vertices.
+ void GetXYZFaceSiTiVertices(S2XYZFaceSiTi* vertices) const;
+
+ // Encode the loop's vertices using S2EncodePointsCompressed. Uses
+ // approximately 8 bytes for the first vertex, going down to less than 4 bytes
+ // per vertex on Google's geographic repository, plus 24 bytes per vertex that
+ // does not correspond to the center of a cell at level 'snap_level'. The loop
+ // vertices must first be converted to the S2XYZFaceSiTi format with
+ // GetXYZFaceSiTiVertices.
+ //
+ // REQUIRES: the loop is initialized and valid.
+ void EncodeCompressed(Encoder* encoder, const S2XYZFaceSiTi* vertices,
+ int snap_level) const;
+
+ // Decode a loop encoded with EncodeCompressed. The parameters must be the
+ // same as the one used when EncodeCompressed was called.
+ bool DecodeCompressed(Decoder* decoder, int snap_level);
+
+ // Returns a bitset of properties used by EncodeCompressed
+ // to efficiently encode boolean values. Properties are
+ // origin_inside and whether the bound was encoded.
+ std::bitset<2> GetCompressedEncodingProperties() const;
+
+ // Given an iterator that is already positioned at the S2ShapeIndexCell
+ // containing "p", returns Contains(p).
+ bool Contains(const MutableS2ShapeIndex::Iterator& it,
+ const S2Point& p) const;
+
+ // Returns true if the loop boundary intersects "target". It may also
+ // return true when the loop boundary does not intersect "target" but
+ // some edge comes within the worst-case error tolerance.
+ //
+ // REQUIRES: it.id().contains(target.id())
+ // [This condition is true whenever it.Locate(target) returns INDEXED.]
+ bool BoundaryApproxIntersects(const MutableS2ShapeIndex::Iterator& it,
+ const S2Cell& target) const;
+
+ // Returns an index "first" and a direction "dir" such that the vertex
+ // sequence (first, first + dir, ..., first + (n - 1) * dir) does not change
+ // when the loop vertex order is rotated or reversed. This allows the loop
+ // vertices to be traversed in a canonical order.
+ S2::LoopOrder GetCanonicalLoopOrder() const;
+
+ // Returns the index of a vertex at point "p", or -1 if not found.
+ // The return value is in the range 1..num_vertices_ if found.
+ int FindVertex(const S2Point& p) const;
+
+ // This method checks all edges of loop A for intersection against all edges
+ // of loop B. If there is any shared vertex, the wedges centered at this
+ // vertex are sent to "relation".
+ //
+ // If the two loop boundaries cross, this method is guaranteed to return
+ // true. It also returns true in certain cases if the loop relationship is
+ // equivalent to crossing. For example, if the relation is Contains() and a
+ // point P is found such that B contains P but A does not contain P, this
+ // method will return true to indicate that the result is the same as though
+ // a pair of crossing edges were found (since Contains() returns false in
+ // both cases).
+ //
+ // See Contains(), Intersects() and CompareBoundary() for the three uses of
+ // this function.
+ static bool HasCrossingRelation(const S2Loop& a, const S2Loop& b,
+ LoopRelation* relation);
+
+ // When the loop is modified (Invert(), or Init() called again) then the
+ // indexing structures need to be cleared since they become invalid.
+ void ClearIndex();
+
+ // The nesting depth, if this field belongs to an S2Polygon. We define it
+ // here to optimize field packing.
+ int depth_ = 0;
+
+ // We store the vertices in an array rather than a vector because we don't
+ // need any STL methods, and computing the number of vertices using size()
+ // would be relatively expensive (due to division by sizeof(S2Point) == 24).
+ // When DecodeWithinScope is used to initialize the loop, we do not
+ // take ownership of the memory for vertices_, and the owns_vertices_ field
+ // is used to prevent deallocation and overwriting.
+ int num_vertices_ = 0;
+ S2Point* vertices_ = nullptr;
+ bool owns_vertices_ = false;
+
+ S2Debug s2debug_override_ = S2Debug::ALLOW;
+ bool origin_inside_ = false; // Does the loop contain S2::Origin()?
+
+ // In general we build the index the first time it is needed, but we make an
+ // exception for Contains(S2Point) because this method has a simple brute
+ // force implementation that is also relatively cheap. For this one method
+ // we keep track of the number of calls made and only build the index once
+ // enough calls have been made that we think an index would be worthwhile.
+ mutable std::atomic<int32> unindexed_contains_calls_;
+
+ // "bound_" is a conservative bound on all points contained by this loop:
+ // if A.Contains(P), then A.bound_.Contains(S2LatLng(P)).
+ S2LatLngRect bound_;
+
+ // Since "bound_" is not exact, it is possible that a loop A contains
+ // another loop B whose bounds are slightly larger. "subregion_bound_"
+ // has been expanded sufficiently to account for this error, i.e.
+ // if A.Contains(B), then A.subregion_bound_.Contains(B.bound_).
+ S2LatLngRect subregion_bound_;
+
+ // Spatial index for this loop.
+ MutableS2ShapeIndex index_;
+
+ // SWIG doesn't understand "= delete".
+#ifndef SWIG
+ void operator=(const S2Loop&) = delete;
+#endif // SWIG
+};
+
+
+//////////////////// Implementation Details Follow ////////////////////////
+
+
+// Any single-vertex loop is interpreted as being either the empty loop or the
+// full loop, depending on whether the vertex is in the northern or southern
+// hemisphere respectively.
+inline S2Point S2Loop::kEmptyVertex() { return S2Point(0, 0, 1); }
+inline S2Point S2Loop::kFullVertex() { return S2Point(0, 0, -1); }
+
+inline std::vector<S2Point> S2Loop::kEmpty() {
+ return std::vector<S2Point>(1, kEmptyVertex());
+}
+
+inline std::vector<S2Point> S2Loop::kFull() {
+ return std::vector<S2Point>(1, kFullVertex());
+}
+
+inline bool S2Loop::is_empty() const {
+ return is_empty_or_full() && !contains_origin();
+}
+
+inline bool S2Loop::is_full() const {
+ return is_empty_or_full() && contains_origin();
+}
+
+inline bool S2Loop::is_empty_or_full() const {
+ return num_vertices() == 1;
+}
+
+// Since this method is templatized and public, the implementation needs to be
+// in the .h file.
+
+template <class T>
+T S2Loop::GetSurfaceIntegral(T f_tri(const S2Point&, const S2Point&,
+ const S2Point&)) const {
+ return S2::GetSurfaceIntegral(vertices_span(), f_tri);
+}
+
+#endif // S2_S2LOOP_H_
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// Defines various angle and area measures for loops on the sphere. These are
+// low-level methods that work directly with arrays of S2Points. They are
+// used to implement the methods in s2shapeindex_measures.h,
+// s2shape_measures.h, s2loop.h, and s2polygon.h.
+//
+// See s2polyline_measures.h, s2edge_distances.h, and s2measures.h for
+// additional low-level methods.
+
+#ifndef S2_S2LOOP_MEASURES_H_
+#define S2_S2LOOP_MEASURES_H_
+
+#include <cmath>
+#include <ostream>
+#include <vector>
+
+#include "s2/s1angle.h"
+#include "s2/s2point.h"
+#include "s2/s2point_span.h"
+#include "s2/s2pointutil.h"
+
+namespace S2 {
+
+// Returns the perimeter of the loop.
+S1Angle GetPerimeter(S2PointLoopSpan loop);
+
+// Returns the area of the loop interior, i.e. the region on the left side of
+// the loop. The result is between 0 and 4*Pi steradians. The implementation
+// ensures that nearly-degenerate clockwise loops have areas close to zero,
+// while nearly-degenerate counter-clockwise loops have areas close to 4*Pi.
+double GetArea(S2PointLoopSpan loop);
+
+// Like GetArea(), except that this method is faster and has more error. The
+// result is between 0 and 4*Pi steradians. The maximum error is 2.22e-15
+// steradians per loop vertex, which works out to about 0.09 square meters per
+// vertex on the Earth's surface. For example, a loop with 100 vertices has a
+// maximum error of about 9 square meters. (The actual error is typically
+// much smaller than this.) The error bound can be computed using
+// GetCurvatureMaxError(), which returns the maximum error in steradians.
+double GetApproxArea(S2PointLoopSpan loop);
+
+// Returns either the positive area of the region on the left side of the
+// loop, or the negative area of the region on the right side of the loop,
+// whichever is smaller in magnitude. The result is between -2*Pi and 2*Pi
+// steradians. This method is used to accurately compute the area of polygons
+// consisting of multiple loops.
+//
+// The following cases are handled specially:
+//
+// - Counter-clockwise loops are guaranteed to have positive area, and
+// clockwise loops are guaranteed to have negative area.
+//
+// - Degenerate loops (consisting of an isolated vertex or composed entirely
+// of sibling edge pairs) have an area of exactly zero.
+//
+// - The full loop (containing all points, and represented as a loop with no
+// vertices) has a negative area with the minimum possible magnitude.
+// (This is the "signed equivalent" of having an area of 4*Pi.)
+double GetSignedArea(S2PointLoopSpan loop);
+
+// Returns the geodesic curvature of the loop, defined as the sum of the turn
+// angles at each vertex (see S2::TurnAngle). The result is positive if the
+// loop is counter-clockwise, negative if the loop is clockwise, and zero if
+// the loop is a great circle. The geodesic curvature is equal to 2*Pi minus
+// the area of the loop.
+//
+// The following cases are handled specially:
+//
+// - Degenerate loops (consisting of an isolated vertex or composed entirely
+// of sibling edge pairs) have a curvature of 2*Pi exactly.
+//
+// - The full loop (containing all points, and represented as a loop with no
+// vertices) has a curvature of -2*Pi exactly.
+//
+// - All other loops have a non-zero curvature in the range (-2*Pi, 2*Pi).
+// For any such loop, reversing the order of the vertices is guaranteed to
+// negate the curvature. This property can be used to define a unique
+// normalized orientation for every loop.
+double GetCurvature(S2PointLoopSpan loop);
+
+// Returns the maximum error in GetCurvature() for the given loop. This value
+// is also an upper bound on the error in GetArea(), GetSignedArea(), and
+// GetApproxArea().
+double GetCurvatureMaxError(S2PointLoopSpan loop);
+
+// Returns the true centroid of the loop multiplied by the area of the loop
+// (see s2centroids.h for details on centroids). The result is not unit
+// length, so you may want to normalize it. Also note that in general, the
+// centroid may not be contained by the loop.
+//
+// The result is scaled by the loop area for two reasons: (1) it is cheaper to
+// compute this way, and (2) it makes it easier to compute the centroid of
+// more complicated shapes (by splitting them into disjoint regions and adding
+// their centroids).
+S2Point GetCentroid(S2PointLoopSpan loop);
+
+// Returns true if the loop area is at most 2*Pi. (A small amount of error is
+// allowed in order to ensure that loops representing an entire hemisphere are
+// always considered normalized.)
+//
+// Degenerate loops are handled consistently with s2pred::Sign(), i.e., if a
+// loop can be expressed as the union of degenerate or nearly-degenerate
+// counter-clockwise triangles then this method will return true.
+bool IsNormalized(S2PointLoopSpan loop);
+
+// LoopOrder represents a cyclic ordering of the loop vertices, starting at
+// the index "first" and proceeding in direction "dir" (either +1 or -1).
+// "first" and "dir" must be chosen such that (first, ..., first + n * dir)
+// are all in the range [0, 2*n-1] as required by S2PointLoopSpan::operator[].
+struct LoopOrder {
+ LoopOrder(int _first, int _dir) : first(_first), dir(_dir) {}
+ int first;
+ int dir;
+};
+bool operator==(LoopOrder x, LoopOrder y);
+std::ostream& operator<<(std::ostream& os, LoopOrder order);
+
+// Returns an index "first" and a direction "dir" such that the vertex
+// sequence (first, first + dir, ..., first + (n - 1) * dir) does not change
+// when the loop vertex order is rotated or reversed. This allows the loop
+// vertices to be traversed in a canonical order.
+LoopOrder GetCanonicalLoopOrder(S2PointLoopSpan loop);
+
+// Returns the oriented surface integral of some quantity f(x) over the loop
+// interior, given a function f_tri(A,B,C) that returns the corresponding
+// integral over the spherical triangle ABC. Here "oriented surface integral"
+// means:
+//
+// (1) f_tri(A,B,C) must be the integral of f if ABC is counterclockwise,
+// and the integral of -f if ABC is clockwise.
+//
+// (2) The result of this function is *either* the integral of f over the
+// loop interior, or the integral of (-f) over the loop exterior.
+//
+// Note that there are at least two common situations where property (2) above
+// is not a limitation:
+//
+// - If the integral of f over the entire sphere is zero, then it doesn't
+// matter which case is returned because they are always equal.
+//
+// - If f is non-negative, then it is easy to detect when the integral over
+// the loop exterior has been returned, and the integral over the loop
+// interior can be obtained by adding the integral of f over the entire
+// unit sphere (a constant) to the result.
+//
+// REQUIRES: The default constructor for T must initialize the value to zero.
+// (This is true for built-in types such as "double".)
+template <class T>
+T GetSurfaceIntegral(S2PointLoopSpan loop,
+ T f_tri(const S2Point&, const S2Point&, const S2Point&));
+
+// Returns a new loop obtained by removing all degeneracies from "loop". In
+// particular, the result will not contain any adjacent duplicate vertices or
+// sibling edge pairs, i.e. vertex sequences of the form (A, A) or (A, B, A).
+//
+// "new_vertices" represents storage where new loop vertices may be written.
+// Note that the S2PointLoopSpan result may be a subsequence of either "loop"
+// or "new_vertices", and therefore "new_vertices" must persist until the
+// result of this method is no longer needed.
+S2PointLoopSpan PruneDegeneracies(S2PointLoopSpan loop,
+ std::vector<S2Point>* new_vertices);
+
+
+//////////////////// Implementation details follow ////////////////////////
+
+
+inline bool operator==(LoopOrder x, LoopOrder y) {
+ return x.first == y.first && x.dir == y.dir;
+}
+
+template <class T>
+T GetSurfaceIntegral(S2PointLoopSpan loop,
+ T f_tri(const S2Point&, const S2Point&, const S2Point&)) {
+ // We sum "f_tri" over a collection T of oriented triangles, possibly
+ // overlapping. Let the sign of a triangle be +1 if it is CCW and -1
+ // otherwise, and let the sign of a point "x" be the sum of the signs of the
+ // triangles containing "x". Then the collection of triangles T is chosen
+ // such that either:
+ //
+ // (1) Each point in the loop interior has sign +1, and sign 0 otherwise; or
+ // (2) Each point in the loop exterior has sign -1, and sign 0 otherwise.
+ //
+ // The triangles basically consist of a "fan" from vertex 0 to every loop
+ // edge that does not include vertex 0. These triangles will always satisfy
+ // either (1) or (2). However, what makes this a bit tricky is that
+ // spherical edges become numerically unstable as their length approaches
+ // 180 degrees. Of course there is not much we can do if the loop itself
+ // contains such edges, but we would like to make sure that all the triangle
+ // edges under our control (i.e., the non-loop edges) are stable. For
+ // example, consider a loop around the equator consisting of four equally
+ // spaced points. This is a well-defined loop, but we cannot just split it
+ // into two triangles by connecting vertex 0 to vertex 2.
+ //
+ // We handle this type of situation by moving the origin of the triangle fan
+ // whenever we are about to create an unstable edge. We choose a new
+ // location for the origin such that all relevant edges are stable. We also
+ // create extra triangles with the appropriate orientation so that the sum
+ // of the triangle signs is still correct at every point.
+
+ // The maximum length of an edge for it to be considered numerically stable.
+ // The exact value is fairly arbitrary since it depends on the stability of
+ // the "f_tri" function. The value below is quite conservative but could be
+ // reduced further if desired.
+ static const double kMaxLength = M_PI - 1e-5;
+
+ // The default constructor for T must initialize the value to zero.
+ // (This is true for built-in types such as "double".)
+ T sum = T();
+ if (loop.size() < 3) return sum;
+
+ S2Point origin = loop[0];
+ for (int i = 1; i + 1 < loop.size(); ++i) {
+ // Let V_i be loop[i], let O be the current origin, and let length(A,B)
+ // be the length of edge (A,B). At the start of each loop iteration, the
+ // "leading edge" of the triangle fan is (O,V_i), and we want to extend
+ // the triangle fan so that the leading edge is (O,V_i+1).
+ //
+ // Invariants:
+ // 1. length(O,V_i) < kMaxLength for all (i > 1).
+ // 2. Either O == V_0, or O is approximately perpendicular to V_0.
+ // 3. "sum" is the oriented integral of f over the area defined by
+ // (O, V_0, V_1, ..., V_i).
+ S2_DCHECK(i == 1 || origin.Angle(loop[i]) < kMaxLength);
+ S2_DCHECK(origin == loop[0] || std::fabs(origin.DotProd(loop[0])) < 1e-15);
+
+ if (loop[i + 1].Angle(origin) > kMaxLength) {
+ // We are about to create an unstable edge, so choose a new origin O'
+ // for the triangle fan.
+ S2Point old_origin = origin;
+ if (origin == loop[0]) {
+ // The following point is well-separated from V_i and V_0 (and
+ // therefore V_i+1 as well).
+ origin = S2::RobustCrossProd(loop[0], loop[i]).Normalize();
+ } else if (loop[i].Angle(loop[0]) < kMaxLength) {
+ // All edges of the triangle (O, V_0, V_i) are stable, so we can
+ // revert to using V_0 as the origin.
+ origin = loop[0];
+ } else {
+ // (O, V_i+1) and (V_0, V_i) are antipodal pairs, and O and V_0 are
+ // perpendicular. Therefore V_0.CrossProd(O) is approximately
+ // perpendicular to all of {O, V_0, V_i, V_i+1}, and we can choose
+ // this point O' as the new origin.
+ origin = loop[0].CrossProd(old_origin);
+
+ // Advance the edge (V_0,O) to (V_0,O').
+ sum += f_tri(loop[0], old_origin, origin);
+ }
+ // Advance the edge (O,V_i) to (O',V_i).
+ sum += f_tri(old_origin, loop[i], origin);
+ }
+ // Advance the edge (O,V_i) to (O,V_i+1).
+ sum += f_tri(origin, loop[i], loop[i+1]);
+ }
+ // If the origin is not V_0, we need to sum one more triangle.
+ if (origin != loop[0]) {
+ // Advance the edge (O,V_n-1) to (O,V_0).
+ sum += f_tri(origin, loop[loop.size() - 1], loop[0]);
+ }
+ return sum;
+}
+
+} // namespace S2
+
+#endif // S2_S2LOOP_MEASURES_H_
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// This file defines a collection of classes that are useful for computing
+// maximum distances on the sphere. Their purpose is to allow code to be
+// shared among the various query classes that find remote geometry, such as
+// S2FurthestPointQuery and S2FurthestEdgeQuery.
+
+#ifndef S2_S2MAX_DISTANCE_TARGETS_H_
+#define S2_S2MAX_DISTANCE_TARGETS_H_
+
+#include <memory>
+
+#include "s2/_fp_contract_off.h"
+#include "s2/s1angle.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s2cell.h"
+#include "s2/s2distance_target.h"
+#include "s2/s2edge_distances.h"
+#include "s2/s2shape_index.h"
+
+class S2FurthestEdgeQuery;
+
+// S2MaxDistance is a class that allows maximum distances to be computed using
+// a minimum distance algorithm. Specifically, S2MaxDistance(x) represents the
+// supplementary distance (Pi - x). This has the effect of inverting the sort
+// order, i.e.
+//
+// (S2MaxDistance(x) < S2MaxDistance(y)) <=> (Pi - x < Pi - y) <=> (x > y)
+//
+// All other operations are implemented similarly (using the supplementary
+// distance Pi - x). For example, S2MaxDistance(x) - S2MaxDistance(y) ==
+// S2MaxDistance(x + y).
+class S2MaxDistance {
+ public:
+ using Delta = S1ChordAngle;
+
+ S2MaxDistance() : distance_() {}
+ explicit S2MaxDistance(S1ChordAngle x) : distance_(x) {}
+ explicit operator S1ChordAngle() const { return distance_; }
+ static S2MaxDistance Zero();
+ static S2MaxDistance Infinity();
+ static S2MaxDistance Negative();
+
+ friend bool operator==(S2MaxDistance x, S2MaxDistance y);
+ friend bool operator<(S2MaxDistance x, S2MaxDistance y);
+
+ friend S2MaxDistance operator-(S2MaxDistance x, S1ChordAngle delta);
+ S1ChordAngle GetChordAngleBound() const;
+
+ // If (dist < *this), updates *this and returns true (used internally).
+ bool UpdateMin(const S2MaxDistance& dist);
+
+ private:
+ S1ChordAngle distance_;
+};
+
+// S2MaxDistanceTarget represents a geometric object to which maximum distances
+// on the sphere are measured.
+//
+// Subtypes are defined below for measuring the distance to a point, an edge,
+// an S2Cell, or an S2ShapeIndex (an arbitrary collection of geometry).
+using S2MaxDistanceTarget = S2DistanceTarget<S2MaxDistance>;
+
+// An S2DistanceTarget subtype for computing the maximum distance to a point.
+class S2MaxDistancePointTarget : public S2MaxDistanceTarget {
+ public:
+ explicit S2MaxDistancePointTarget(const S2Point& point);
+
+ S2Cap GetCapBound() final;
+ bool UpdateMinDistance(const S2Point& p, S2MaxDistance* min_dist) final;
+ bool UpdateMinDistance(const S2Point& v0, const S2Point& v1,
+ S2MaxDistance* min_dist) final;
+ bool UpdateMinDistance(const S2Cell& cell,
+ S2MaxDistance* min_dist) final;
+ bool VisitContainingShapes(const S2ShapeIndex& index,
+ const ShapeVisitor& visitor) final;
+
+ private:
+ S2Point point_;
+};
+
+// An S2DistanceTarget subtype for computing the maximum distance to an edge.
+class S2MaxDistanceEdgeTarget : public S2MaxDistanceTarget {
+ public:
+ explicit S2MaxDistanceEdgeTarget(const S2Point& a, const S2Point& b);
+
+ S2Cap GetCapBound() final;
+ bool UpdateMinDistance(const S2Point& p, S2MaxDistance* min_dist) final;
+ bool UpdateMinDistance(const S2Point& v0, const S2Point& v1,
+ S2MaxDistance* min_dist) final;
+ bool UpdateMinDistance(const S2Cell& cell,
+ S2MaxDistance* min_dist) final;
+ bool VisitContainingShapes(const S2ShapeIndex& index,
+ const ShapeVisitor& visitor) final;
+
+ private:
+ S2Point a_, b_;
+};
+
+// An S2DistanceTarget subtype for computing the maximum distance to an S2Cell
+// (including the interior of the cell).
+class S2MaxDistanceCellTarget : public S2MaxDistanceTarget {
+ public:
+ explicit S2MaxDistanceCellTarget(const S2Cell& cell);
+ S2Cap GetCapBound() final;
+ bool UpdateMinDistance(const S2Point& p, S2MaxDistance* min_dist) final;
+ bool UpdateMinDistance(const S2Point& v0, const S2Point& v1,
+ S2MaxDistance* min_dist) final;
+ bool UpdateMinDistance(const S2Cell& cell,
+ S2MaxDistance* min_dist) final;
+ bool VisitContainingShapes(const S2ShapeIndex& index,
+ const ShapeVisitor& visitor) final;
+
+ private:
+ S2Cell cell_;
+};
+
+// An S2DistanceTarget subtype for computing the maximum distance to an
+// S2ShapeIndex (a collection of points, polylines, and/or polygons).
+//
+// Note that ShapeIndexTarget has its own options:
+//
+// include_interiors()
+// - specifies that distances are measured to the boundary and interior
+// of polygons in the S2ShapeIndex. (If set to false, distance is
+// measured to the polygon boundary only.)
+// DEFAULT: true.
+//
+// brute_force()
+// - specifies that the distances should be computed by examining every
+// edge in the S2ShapeIndex (for testing and debugging purposes).
+// DEFAULT: false.
+//
+// These options are specified independently of the corresponding
+// S2FurthestEdgeQuery options. For example, if include_interiors is true for
+// a ShapeIndexTarget but false for the S2FurthestEdgeQuery where the target
+// is used, then distances will be measured from the boundary of one
+// S2ShapeIndex to the boundary and interior of the other.
+//
+class S2MaxDistanceShapeIndexTarget : public S2MaxDistanceTarget {
+ public:
+ explicit S2MaxDistanceShapeIndexTarget(const S2ShapeIndex* index);
+ ~S2MaxDistanceShapeIndexTarget() override;
+
+ // Specifies that distance will be measured to the boundary and interior
+ // of polygons in the S2ShapeIndex rather than to polygon boundaries only.
+ //
+ // DEFAULT: true
+ bool include_interiors() const;
+ void set_include_interiors(bool include_interiors);
+
+ // Specifies that the distances should be computed by examining every edge
+ // in the S2ShapeIndex (for testing and debugging purposes).
+ //
+ // DEFAULT: false
+ bool use_brute_force() const;
+ void set_use_brute_force(bool use_brute_force);
+
+ bool set_max_error(const S1ChordAngle& max_error) override;
+ S2Cap GetCapBound() final;
+ bool UpdateMinDistance(const S2Point& p, S2MaxDistance* min_dist) final;
+ bool UpdateMinDistance(const S2Point& v0, const S2Point& v1,
+ S2MaxDistance* min_dist) final;
+ bool UpdateMinDistance(const S2Cell& cell,
+ S2MaxDistance* min_dist) final;
+ bool VisitContainingShapes(const S2ShapeIndex& query_index,
+ const ShapeVisitor& visitor) final;
+
+ private:
+ const S2ShapeIndex* index_;
+ std::unique_ptr<S2FurthestEdgeQuery> query_;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+inline S2MaxDistance S2MaxDistance::Zero() {
+ return S2MaxDistance(S1ChordAngle::Straight());
+}
+
+inline S2MaxDistance S2MaxDistance::Infinity() {
+ return S2MaxDistance(S1ChordAngle::Negative());
+}
+
+inline S2MaxDistance S2MaxDistance::Negative() {
+ return S2MaxDistance(S1ChordAngle::Infinity());
+}
+
+inline bool operator==(S2MaxDistance x, S2MaxDistance y) {
+ return x.distance_ == y.distance_;
+}
+
+inline bool operator<(S2MaxDistance x, S2MaxDistance y) {
+ return x.distance_ > y.distance_;
+}
+
+inline S2MaxDistance operator-(S2MaxDistance x, S1ChordAngle delta) {
+ return S2MaxDistance(x.distance_ + delta);
+}
+
+inline S1ChordAngle S2MaxDistance::GetChordAngleBound() const {
+ return S1ChordAngle::Straight() - distance_;
+}
+
+inline bool S2MaxDistance::UpdateMin(const S2MaxDistance& dist) {
+ if (dist < *this) {
+ *this = dist;
+ return true;
+ }
+ return false;
+}
+
+inline S2MaxDistancePointTarget::S2MaxDistancePointTarget(const S2Point& point)
+ : point_(point) {
+}
+
+inline S2MaxDistanceEdgeTarget::S2MaxDistanceEdgeTarget(const S2Point& a,
+ const S2Point& b)
+ : a_(a), b_(b) {
+ a_.Normalize();
+ b_.Normalize();
+}
+
+inline S2MaxDistanceCellTarget::S2MaxDistanceCellTarget(const S2Cell& cell)
+ : cell_(cell) {
+}
+
+#endif // S2_S2MAX_DISTANCE_TARGETS_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// Defines various angle and area measures on the sphere. Also see
+// s2edge_distances.h, s2loop_measures.h, and s2polyline_measures.h.
+
+#ifndef S2_S2MEASURES_H_
+#define S2_S2MEASURES_H_
+
+#include "s2/s2point.h"
+
+namespace S2 {
+
+// Return the interior angle at the vertex B in the triangle ABC. The
+// return value is always in the range [0, Pi]. All points should be
+// normalized. Ensures that Angle(a,b,c) == Angle(c,b,a) for all a,b,c.
+//
+// The angle is undefined if A or C is diametrically opposite from B, and
+// becomes numerically unstable as the length of edge AB or BC approaches
+// 180 degrees.
+double Angle(const S2Point& a, const S2Point& b, const S2Point& c);
+
+// Return the exterior angle at vertex B in the triangle ABC. The return
+// value is positive if ABC is counterclockwise and negative otherwise. If
+// you imagine an ant walking from A to B to C, this is the angle that the
+// ant turns at vertex B (positive = left = CCW, negative = right = CW).
+// This quantity is also known as the "geodesic curvature" at B.
+//
+// Ensures that TurnAngle(a,b,c) == -TurnAngle(c,b,a) for all distinct
+// a,b,c. The result is undefined if (a == b || b == c), but is either
+// -Pi or Pi if (a == c). All points should be normalized.
+double TurnAngle(const S2Point& a, const S2Point& b, const S2Point& c);
+
+// Return the area of triangle ABC. This method combines two different
+// algorithms to get accurate results for both large and small triangles.
+// The maximum error is about 5e-15 (about 0.25 square meters on the Earth's
+// surface), the same as GirardArea() below, but unlike that method it is
+// also accurate for small triangles. Example: when the true area is 100
+// square meters, Area() yields an error about 1 trillion times smaller than
+// GirardArea().
+//
+// All points should be unit length, and no two points should be antipodal.
+// The area is always positive.
+double Area(const S2Point& a, const S2Point& b, const S2Point& c);
+
+// Return the area of the triangle computed using Girard's formula. All
+// points should be unit length, and no two points should be antipodal.
+//
+// This method is about twice as fast as Area() but has poor relative
+// accuracy for small triangles. The maximum error is about 5e-15 (about
+// 0.25 square meters on the Earth's surface) and the average error is about
+// 1e-15. These bounds apply to triangles of any size, even as the maximum
+// edge length of the triangle approaches 180 degrees. But note that for
+// such triangles, tiny perturbations of the input points can change the
+// true mathematical area dramatically.
+double GirardArea(const S2Point& a, const S2Point& b, const S2Point& c);
+
+// Like Area(), but returns a positive value for counterclockwise triangles
+// and a negative value otherwise.
+double SignedArea(const S2Point& a, const S2Point& b, const S2Point& c);
+
+} // namespace S2
+
+#endif // S2_S2MEASURES_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// The following are various constants that describe the shapes and sizes of
+// S2Cells (see s2coords.h and s2cell_id.h). They are useful for deciding
+// which cell level to use in order to satisfy a given condition (e.g. that
+// cell vertices must be no further than "x" apart). All of the raw constants
+// are differential quantities; you can use the GetValue(level) method to
+// compute the corresponding length or area on the unit sphere for cells at a
+// given level. The minimum and maximum bounds are valid for cells at all
+// levels, but they may be somewhat conservative for very large cells
+// (e.g. face cells).
+
+#ifndef S2_S2METRICS_H_
+#define S2_S2METRICS_H_
+
+#include <algorithm>
+#include <cmath>
+
+#include "s2/s2coords.h"
+#include "s2/util/math/mathutil.h"
+
+namespace S2 {
+
+// Defines a cell metric of the given dimension (1 == length, 2 == area).
+template <int dim> class Metric {
+ public:
+ explicit constexpr Metric(double deriv) : deriv_(deriv) {}
+
+ // The "deriv" value of a metric is a derivative, and must be multiplied by
+ // a length or area in (s,t)-space to get a useful value.
+ double deriv() const { return deriv_; }
+
+ // Return the value of a metric for cells at the given level. The value is
+ // either a length or an area on the unit sphere, depending on the
+ // particular metric.
+ double GetValue(int level) const { return ldexp(deriv_, - dim * level); }
+
+ // Return the level at which the metric has approximately the given
+ // value. For example, S2::kAvgEdge.GetClosestLevel(0.1) returns the
+ // level at which the average cell edge length is approximately 0.1.
+ // The return value is always a valid level.
+ int GetClosestLevel(double value) const;
+
+ // Return the minimum level such that the metric is at most the given
+ // value, or S2CellId::kMaxLevel if there is no such level. For example,
+ // S2::kMaxDiag.GetLevelForMaxValue(0.1) returns the minimum level such
+ // that all cell diagonal lengths are 0.1 or smaller. The return value
+ // is always a valid level.
+ int GetLevelForMaxValue(double value) const;
+
+ // Return the maximum level such that the metric is at least the given
+ // value, or zero if there is no such level. For example,
+ // S2::kMinWidth.GetLevelForMinValue(0.1) returns the maximum level such
+ // that all cells have a minimum width of 0.1 or larger. The return value
+ // is always a valid level.
+ int GetLevelForMinValue(double value) const;
+
+ private:
+ const double deriv_;
+
+ Metric(const Metric&) = delete;
+ void operator=(const Metric&) = delete;
+};
+using LengthMetric = Metric<1>;
+using AreaMetric = Metric<2>;
+
+// Each cell is bounded by four planes passing through its four edges and
+// the center of the sphere. These metrics relate to the angle between each
+// pair of opposite bounding planes, or equivalently, between the planes
+// corresponding to two different s-values or two different t-values. For
+// example, the maximum angle between opposite bounding planes for a cell at
+// level k is kMaxAngleSpan.GetValue(k), and the average angle span for all
+// cells at level k is approximately kAvgAngleSpan.GetValue(k).
+extern const LengthMetric kMinAngleSpan;
+extern const LengthMetric kMaxAngleSpan;
+extern const LengthMetric kAvgAngleSpan;
+
+// The width of geometric figure is defined as the distance between two
+// parallel bounding lines in a given direction. For cells, the minimum
+// width is always attained between two opposite edges, and the maximum
+// width is attained between two opposite vertices. However, for our
+// purposes we redefine the width of a cell as the perpendicular distance
+// between a pair of opposite edges. A cell therefore has two widths, one
+// in each direction. The minimum width according to this definition agrees
+// with the classic geometric one, but the maximum width is different. (The
+// maximum geometric width corresponds to kMaxDiag defined below.)
+//
+// For a cell at level k, the distance between opposite edges is at least
+// kMinWidth.GetValue(k) and at most kMaxWidth.GetValue(k). The average
+// width in both directions for all cells at level k is approximately
+// kAvgWidth.GetValue(k).
+//
+// The width is useful for bounding the minimum or maximum distance from a
+// point on one edge of a cell to the closest point on the opposite edge.
+// For example, this is useful when "growing" regions by a fixed distance.
+//
+// Note that because S2Cells are not usually rectangles, the minimum width of
+// a cell is generally smaller than its minimum edge length. (The interior
+// angles of an S2Cell range from 60 to 120 degrees.)
+extern const LengthMetric kMinWidth;
+extern const LengthMetric kMaxWidth;
+extern const LengthMetric kAvgWidth;
+
+// The minimum edge length of any cell at level k is at least
+// kMinEdge.GetValue(k), and the maximum is at most kMaxEdge.GetValue(k).
+// The average edge length is approximately kAvgEdge.GetValue(k).
+//
+// The edge length metrics can also be used to bound the minimum, maximum,
+// or average distance from the center of one cell to the center of one of
+// its edge neighbors. In particular, it can be used to bound the distance
+// between adjacent cell centers along the space-filling Hilbert curve for
+// cells at any given level.
+extern const LengthMetric kMinEdge;
+extern const LengthMetric kMaxEdge;
+extern const LengthMetric kAvgEdge;
+
+// The minimum diagonal length of any cell at level k is at least
+// kMinDiag.GetValue(k), and the maximum is at most kMaxDiag.GetValue(k).
+// The average diagonal length is approximately kAvgDiag.GetValue(k).
+//
+// The maximum diagonal also happens to be the maximum diameter of any cell,
+// and also the maximum geometric width (see the discussion above). So for
+// example, the distance from an arbitrary point to the closest cell center
+// at a given level is at most half the maximum diagonal length.
+extern const LengthMetric kMinDiag;
+extern const LengthMetric kMaxDiag;
+extern const LengthMetric kAvgDiag;
+
+// The minimum area of any cell at level k is at least kMinArea.GetValue(k),
+// and the maximum is at most kMaxArea.GetValue(k). The average area of all
+// cells at level k is exactly kAvgArea.GetValue(k).
+extern const AreaMetric kMinArea;
+extern const AreaMetric kMaxArea;
+extern const AreaMetric kAvgArea;
+
+// This is the maximum edge aspect ratio over all cells at any level, where
+// the edge aspect ratio of a cell is defined as the ratio of its longest
+// edge length to its shortest edge length.
+extern const double kMaxEdgeAspect;
+
+// This is the maximum diagonal aspect ratio over all cells at any level,
+// where the diagonal aspect ratio of a cell is defined as the ratio of its
+// longest diagonal length to its shortest diagonal length.
+extern const double kMaxDiagAspect;
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+template <int dim>
+int S2::Metric<dim>::GetLevelForMaxValue(double value) const {
+ if (value <= 0) return S2::kMaxCellLevel;
+
+ // This code is equivalent to computing a floating-point "level"
+ // value and rounding up. ilogb() returns the exponent corresponding to a
+ // fraction in the range [1,2).
+ int level = ilogb(value / deriv_);
+ level = std::max(0, std::min(S2::kMaxCellLevel, -(level >> (dim - 1))));
+ S2_DCHECK(level == S2::kMaxCellLevel || GetValue(level) <= value);
+ S2_DCHECK(level == 0 || GetValue(level - 1) > value);
+ return level;
+}
+
+template <int dim>
+int S2::Metric<dim>::GetLevelForMinValue(double value) const {
+ if (value <= 0) return S2::kMaxCellLevel;
+
+ // This code is equivalent to computing a floating-point "level"
+ // value and rounding down.
+ int level = ilogb(deriv_ / value);
+ level = std::max(0, std::min(S2::kMaxCellLevel, level >> (dim - 1)));
+ S2_DCHECK(level == 0 || GetValue(level) >= value);
+ S2_DCHECK(level == kMaxCellLevel || GetValue(level + 1) < value);
+ return level;
+}
+
+template <int dim>
+int Metric<dim>::GetClosestLevel(double value) const {
+ return GetLevelForMaxValue((dim == 1 ? M_SQRT2 : 2) * value);
+}
+
+} // namespace S2
+
+#endif // S2_S2METRICS_H_
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// This file defines a collection of classes that are useful for computing
+// minimum distances on the sphere. Their purpose is to allow code to be
+// shared among the various query classes that find nearby geometry, such as
+// S2ClosestEdgeQuery, S2ClosestPointQuery, and S2ClosestCellQuery.
+
+#ifndef S2_S2MIN_DISTANCE_TARGETS_H_
+#define S2_S2MIN_DISTANCE_TARGETS_H_
+
+#include <memory>
+
+#include "s2/_fp_contract_off.h"
+#include "s2/s1angle.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s2cell.h"
+#include "s2/s2cell_index.h"
+#include "s2/s2distance_target.h"
+#include "s2/s2edge_distances.h"
+#include "s2/s2shape_index.h"
+
+// Forward references because these classes depend on the types defined here.
+class S2ClosestCellQuery;
+class S2ClosestEdgeQuery;
+
+// S2MinDistance is a thin wrapper around S1ChordAngle that is used by classes
+// such as S2ClosestEdgeQuery to compute minimum distances on the sphere (as
+// opposed to maximum distances, ellipsoidal distances, etc).
+//
+// It implements the Distance concept defined by S2DistanceTarget (see
+// s2distance_target.h for details).
+class S2MinDistance : public S1ChordAngle {
+ public:
+ using Delta = S1ChordAngle;
+
+ S2MinDistance() : S1ChordAngle() {}
+ explicit S2MinDistance(S1Angle x) : S1ChordAngle(x) {}
+ explicit S2MinDistance(S1ChordAngle x) : S1ChordAngle(x) {}
+ static S2MinDistance Zero();
+ static S2MinDistance Infinity();
+ static S2MinDistance Negative();
+ friend S2MinDistance operator-(S2MinDistance x, S1ChordAngle delta);
+ S1ChordAngle GetChordAngleBound() const;
+
+ // If (dist < *this), updates *this and returns true (used internally).
+ bool UpdateMin(const S2MinDistance& dist);
+};
+
+// S2MinDistanceTarget represents a geometric object to which distances are
+// measured. Specifically, it is used to compute minimum distances on the
+// sphere (as opposed to maximum distances, ellipsoidal distances, etc).
+//
+// Subtypes are defined below for measuring the distance to a point, an edge,
+// an S2Cell, or an S2ShapeIndex (an arbitrary collection of geometry).
+using S2MinDistanceTarget = S2DistanceTarget<S2MinDistance>;
+
+// An S2DistanceTarget subtype for computing the minimum distance to a point.
+class S2MinDistancePointTarget : public S2MinDistanceTarget {
+ public:
+ explicit S2MinDistancePointTarget(const S2Point& point);
+ S2Cap GetCapBound() final;
+ bool UpdateMinDistance(const S2Point& p, S2MinDistance* min_dist) final;
+ bool UpdateMinDistance(const S2Point& v0, const S2Point& v1,
+ S2MinDistance* min_dist) final;
+ bool UpdateMinDistance(const S2Cell& cell,
+ S2MinDistance* min_dist) final;
+ bool VisitContainingShapes(const S2ShapeIndex& index,
+ const ShapeVisitor& visitor) final;
+
+ private:
+ S2Point point_;
+};
+
+// An S2DistanceTarget subtype for computing the minimum distance to a edge.
+class S2MinDistanceEdgeTarget : public S2MinDistanceTarget {
+ public:
+ S2MinDistanceEdgeTarget(const S2Point& a, const S2Point& b);
+ S2Cap GetCapBound() final;
+ bool UpdateMinDistance(const S2Point& p, S2MinDistance* min_dist) final;
+ bool UpdateMinDistance(const S2Point& v0, const S2Point& v1,
+ S2MinDistance* min_dist) final;
+ bool UpdateMinDistance(const S2Cell& cell,
+ S2MinDistance* min_dist) final;
+ bool VisitContainingShapes(const S2ShapeIndex& index,
+ const ShapeVisitor& visitor) final;
+
+ private:
+ S2Point a_, b_;
+};
+
+// An S2DistanceTarget subtype for computing the minimum distance to an S2Cell
+// (including the interior of the cell).
+class S2MinDistanceCellTarget : public S2MinDistanceTarget {
+ public:
+ explicit S2MinDistanceCellTarget(const S2Cell& cell);
+ S2Cap GetCapBound() final;
+ bool UpdateMinDistance(const S2Point& p, S2MinDistance* min_dist) final;
+ bool UpdateMinDistance(const S2Point& v0, const S2Point& v1,
+ S2MinDistance* min_dist) final;
+ bool UpdateMinDistance(const S2Cell& cell,
+ S2MinDistance* min_dist) final;
+ bool VisitContainingShapes(const S2ShapeIndex& index,
+ const ShapeVisitor& visitor) final;
+
+ private:
+ S2Cell cell_;
+};
+
+// An S2DistanceTarget subtype for computing the minimum distance to an
+// S2CellUnion (including the interior of all cells).
+class S2MinDistanceCellUnionTarget : public S2MinDistanceTarget {
+ public:
+ explicit S2MinDistanceCellUnionTarget(S2CellUnion cell_union);
+ ~S2MinDistanceCellUnionTarget() override;
+
+ // Specifies that the distances should be computed by examining every cell
+ // in the S2CellIndex (for testing and debugging purposes).
+ //
+ // DEFAULT: false
+ bool use_brute_force() const;
+ void set_use_brute_force(bool use_brute_force);
+
+ // Note that set_max_error() should not be called directly by clients; it is
+ // used internally by the S2Closest*Query implementations.
+ bool set_max_error(const S1ChordAngle& max_error) override;
+
+ S2Cap GetCapBound() final;
+ bool UpdateMinDistance(const S2Point& p, S2MinDistance* min_dist) final;
+ bool UpdateMinDistance(const S2Point& v0, const S2Point& v1,
+ S2MinDistance* min_dist) final;
+ bool UpdateMinDistance(const S2Cell& cell,
+ S2MinDistance* min_dist) final;
+ bool VisitContainingShapes(const S2ShapeIndex& query_index,
+ const ShapeVisitor& visitor) final;
+
+ private:
+ bool UpdateMinDistance(S2MinDistanceTarget* target, S2MinDistance* min_dist);
+
+ S2CellUnion cell_union_;
+ S2CellIndex index_;
+ std::unique_ptr<S2ClosestCellQuery> query_;
+};
+
+// An S2DistanceTarget subtype for computing the minimum distance to an
+// S2ShapeIndex (a collection of points, polylines, and/or polygons).
+//
+// Note that ShapeIndexTarget has its own options:
+//
+// include_interiors()
+// - specifies that distances are measured to the boundary and interior
+// of polygons in the S2ShapeIndex. (If set to false, distance is
+// measured to the polygon boundary only.)
+// DEFAULT: true.
+//
+// brute_force()
+// - specifies that the distances should be computed by examining every
+// edge in the S2ShapeIndex (for testing and debugging purposes).
+// DEFAULT: false.
+//
+// These options are specified independently of the corresponding
+// S2ClosestEdgeQuery options. For example, if include_interiors is true for
+// a ShapeIndexTarget but false for the S2ClosestEdgeQuery where the target
+// is used, then distances will be measured from the boundary of one
+// S2ShapeIndex to the boundary and interior of the other.
+//
+// Note that when the distance to a ShapeIndexTarget is zero because the
+// target intersects the interior of the query index, you can find a point
+// that achieves this zero distance by calling the VisitContainingShapes()
+// method directly. For example:
+//
+// S2ClosestEdgeQuery::ShapeIndexTarget target(&target_index);
+// target.VisitContainingShapes(
+// query_index, [](S2Shape* containing_shape,
+// const S2Point& target_point) {
+// ... do something with "target_point" ...
+// return false; // Terminate search
+// }));
+class S2MinDistanceShapeIndexTarget : public S2MinDistanceTarget {
+ public:
+ explicit S2MinDistanceShapeIndexTarget(const S2ShapeIndex* index);
+ ~S2MinDistanceShapeIndexTarget() override;
+
+ // Specifies that distance will be measured to the boundary and interior
+ // of polygons in the S2ShapeIndex rather than to polygon boundaries only.
+ //
+ // DEFAULT: true
+ bool include_interiors() const;
+ void set_include_interiors(bool include_interiors);
+
+ // Specifies that the distances should be computed by examining every edge
+ // in the S2ShapeIndex (for testing and debugging purposes).
+ //
+ // DEFAULT: false
+ bool use_brute_force() const;
+ void set_use_brute_force(bool use_brute_force);
+
+ // Note that set_max_error() should not be called directly by clients; it is
+ // used internally by the S2Closest*Query implementations.
+ bool set_max_error(const S1ChordAngle& max_error) override;
+
+ S2Cap GetCapBound() final;
+ bool UpdateMinDistance(const S2Point& p, S2MinDistance* min_dist) final;
+ bool UpdateMinDistance(const S2Point& v0, const S2Point& v1,
+ S2MinDistance* min_dist) final;
+ bool UpdateMinDistance(const S2Cell& cell,
+ S2MinDistance* min_dist) final;
+ bool VisitContainingShapes(const S2ShapeIndex& query_index,
+ const ShapeVisitor& visitor) final;
+
+ private:
+ bool UpdateMinDistance(S2MinDistanceTarget* target, S2MinDistance* min_dist);
+
+ const S2ShapeIndex* index_;
+ std::unique_ptr<S2ClosestEdgeQuery> query_;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline S2MinDistance S2MinDistance::Zero() {
+ return S2MinDistance(S1ChordAngle::Zero());
+}
+
+inline S2MinDistance S2MinDistance::Infinity() {
+ return S2MinDistance(S1ChordAngle::Infinity());
+}
+
+inline S2MinDistance S2MinDistance::Negative() {
+ return S2MinDistance(S1ChordAngle::Negative());
+}
+
+inline S2MinDistance operator-(S2MinDistance x, S1ChordAngle delta) {
+ return S2MinDistance(S1ChordAngle(x) - delta);
+}
+
+inline S1ChordAngle S2MinDistance::GetChordAngleBound() const {
+ return PlusError(GetS1AngleConstructorMaxError());
+}
+
+inline bool S2MinDistance::UpdateMin(const S2MinDistance& dist) {
+ if (dist < *this) {
+ *this = dist;
+ return true;
+ }
+ return false;
+}
+
+inline S2MinDistancePointTarget::S2MinDistancePointTarget(const S2Point& point)
+ : point_(point) {
+}
+
+inline S2MinDistanceEdgeTarget::S2MinDistanceEdgeTarget(const S2Point& a,
+ const S2Point& b)
+ : a_(a), b_(b) {
+}
+
+#endif // S2_S2MIN_DISTANCE_TARGETS_H_
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2PADDED_CELL_H_
+#define S2_S2PADDED_CELL_H_
+
+#include "s2/_fp_contract_off.h"
+#include "s2/r2rect.h"
+#include "s2/s2cell_id.h"
+
+// S2PaddedCell represents an S2Cell whose (u,v)-range has been expanded on
+// all sides by a given amount of "padding". Unlike S2Cell, its methods and
+// representation are optimized for clipping edges against S2Cell boundaries
+// to determine which cells are intersected by a given set of edges.
+//
+// This class is intended to be copied by value as desired.
+class S2PaddedCell {
+ public:
+ // Construct an S2PaddedCell for the given cell id and padding.
+ S2PaddedCell(S2CellId id, double padding);
+
+ // Construct the child of "parent" with the given (i,j) index. The four
+ // child cells have indices of (0,0), (0,1), (1,0), (1,1), where the i and j
+ // indices correspond to increasing u- and v-values respectively.
+ S2PaddedCell(const S2PaddedCell& parent, int i, int j);
+
+ S2CellId id() const { return id_; }
+ double padding() const { return padding_; }
+ int level() const { return level_; }
+
+ // Return the bound for this cell (including padding).
+ const R2Rect& bound() const { return bound_; }
+
+ // Return the "middle" of the padded cell, defined as the rectangle that
+ // belongs to all four children.
+ //
+ // Note that this method is *not* thread-safe, because the return value is
+ // computed on demand and cached. (It is expected that this class will be
+ // mainly useful in the context of single-threaded recursive algorithms.)
+ const R2Rect& middle() const;
+
+ // Return the (i,j) coordinates for the child cell at the given traversal
+ // position. The traversal position corresponds to the order in which child
+ // cells are visited by the Hilbert curve.
+ void GetChildIJ(int pos, int* i, int* j) const;
+
+ // Return the smallest cell that contains all descendants of this cell whose
+ // bounds intersect "rect". For algorithms that use recursive subdivision
+ // to find the cells that intersect a particular object, this method can be
+ // used to skip all the initial subdivision steps where only one child needs
+ // to be expanded.
+ //
+ // Note that this method is not the same as returning the smallest cell that
+ // contains the intersection of this cell with "rect". Because of the
+ // padding, even if one child completely contains "rect" it is still
+ // possible that a neighboring child also intersects "rect".
+ //
+ // REQUIRES: bound().Intersects(rect)
+ S2CellId ShrinkToFit(const R2Rect& rect) const;
+
+ // Return the center of this cell.
+ S2Point GetCenter() const;
+
+ // Return the vertex where the S2 space-filling curve enters this cell.
+ S2Point GetEntryVertex() const;
+
+ // Return the vertex where the S2 space-filling curve exits this cell.
+ S2Point GetExitVertex() const;
+
+ private:
+ S2CellId id_;
+ double padding_;
+ R2Rect bound_; // Bound in (u,v)-space
+
+ // The rectangle in (u,v)-space that belongs to all four padded children.
+ // It is computed on demand by the middle() accessor method.
+ mutable R2Rect middle_;
+
+ int ij_lo_[2]; // Minimum (i,j)-coordinates of this cell, before padding
+ int orientation_; // Hilbert curve orientation of this cell (see s2coords.h)
+ int level_; // Level of this cell (see s2coords.h)
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline void S2PaddedCell::GetChildIJ(int pos, int* i, int* j) const {
+ int ij = S2::internal::kPosToIJ[orientation_][pos];
+ *i = ij >> 1;
+ *j = ij & 1;
+}
+
+#endif // S2_S2PADDED_CELL_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2POINT_H_
+#define S2_S2POINT_H_
+
+#include "s2/_fp_contract_off.h"
+#include "s2/util/math/vector.h" // IWYU pragma: export
+#include "s2/util/math/vector3_hash.h"
+
+// An S2Point represents a point on the unit sphere as a 3D vector. Usually
+// points are normalized to be unit length, but some methods do not require
+// this. See util/math/vector.h for the methods available. Among other
+// things, there are overloaded operators that make it convenient to write
+// arithmetic expressions (e.g. (1-x)*p1 + x*p2).
+using S2Point = Vector3_d;
+
+// S2PointHash can be used with standard containers (e.g., unordered_set) or
+// nonstandard extensions (e.g., hash_map). It is defined such that if two
+// S2Points compare equal to each other, they have the same hash. (This
+// requires that positive and negative zero hash to the same value.)
+using S2PointHash = GoodFastHash<S2Point>;
+
+#endif // S2_S2POINT_H_
--- /dev/null
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+//
+// Given a sequence of S2Points assumed to be the center of level-k cells,
+// compresses it into a stream using the following method:
+// - decompose the points into (face, si, ti) tuples (see s2coords.h)
+// - run-length encode the faces, combining face number and count into a
+// varint32. See the Faces class in s2point_compression.cc.
+// - right shift the (si, ti) to remove the part that's constant for all cells
+// of level-k. The result is called the (pi, qi) space.
+// - 2nd derivative encode the pi and qi sequences (linear prediction)
+// - zig-zag encode all derivative values but the first, which cannot be
+// negative
+// - interleave the zig-zag encoded values
+// - encode the first interleaved value in a fixed length encoding
+// (varint would make this value larger)
+// - encode the remaining interleaved values as varint64s, as the
+// derivative encoding should make the values small.
+// In addition, provides a lossless method to compress a sequence of points even
+// if some points are not the center of level-k cells. These points are stored
+// exactly, using 3 double precision values, after the above encoded string,
+// together with their index in the sequence (this leads to some redundancy - it
+// is expected that only a small fraction of the points are not cell centers).
+//
+// Require that the encoder was constructed with the no-arg constructor, as
+// Ensure() will be called to allocate space.
+
+//
+// To encode leaf cells, this requires 8 bytes for the first vertex plus
+// an average of 3.8 bytes for each additional vertex, when computed on
+// Google's geographic repository.
+
+#ifndef S2_S2POINT_COMPRESSION_H_
+#define S2_S2POINT_COMPRESSION_H_
+
+#include "s2/third_party/absl/types/span.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/s1angle.h"
+
+class Decoder;
+class Encoder;
+
+// The XYZ and face,si,ti coordinates of an S2Point and, if this point is equal
+// to the center of an S2Cell, the level of this cell (-1 otherwise).
+struct S2XYZFaceSiTi {
+ S2Point xyz;
+ int face;
+ unsigned int si;
+ unsigned int ti;
+ int cell_level;
+};
+
+// Encode the points in the encoder, using an optimized compressed format for
+// points at the center of a cell at 'level', plus 3 double values for the
+// others.
+void S2EncodePointsCompressed(absl::Span<const S2XYZFaceSiTi> points,
+ int level, Encoder* encoder);
+
+// Decode points encoded with S2EncodePointsCompressed. Requires that the
+// level is the level that was used in S2EncodePointsCompressed. Ensures
+// that the decoded points equal the encoded points. Returns true on success.
+bool S2DecodePointsCompressed(Decoder* decoder, int level,
+ absl::Span<S2Point> points);
+
+#endif // S2_S2POINT_COMPRESSION_H_
--- /dev/null
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2POINT_INDEX_H_
+#define S2_S2POINT_INDEX_H_
+
+#include <tuple>
+#include <type_traits>
+#include "s2/util/gtl/btree_map.h"
+#include "s2/s2cell_id.h"
+
+// S2PointIndex maintains an index of points sorted by leaf S2CellId. Each
+// point can optionally store auxiliary data such as an integer or pointer.
+// This can be used to map results back to client data structures.
+//
+// The class supports adding or removing points dynamically, and provides a
+// seekable iterator interface for navigating the index.
+//
+// You can use this class in conjuction with S2ClosestPointQuery to find the
+// closest index points to a given query point. For example:
+//
+// void Test(const vector<S2Point>& index_points,
+// const vector<S2Point>& target_points) {
+// // The template argument allows auxiliary data to be attached to each
+// // point (in this case, the array index).
+// S2PointIndex<int> index;
+// for (int i = 0; i < index_points.size(); ++i) {
+// index.Add(index_points[i], i);
+// }
+// S2ClosestPointQuery<int> query(&index);
+// query.mutable_options()->set_max_results(5);
+// for (const S2Point& target_point : target_points) {
+// S2ClosestPointQueryPointTarget target(target_point);
+// for (const auto& result : query.FindClosestPoints(&target)) {
+// // The Result class contains the following methods:
+// // distance() is the distance to the target.
+// // point() is the indexed point.
+// // data() is the auxiliary data.
+// DoSomething(target_point, result);
+// }
+// }
+// }
+//
+// The Data argument defaults to an empty class, which uses no additional
+// space beyond the S2Point itself. In this case the Data argument is
+// required. For example:
+//
+// S2PointIndex<> index;
+// index.Add(point);
+//
+// Points can be added or removed from the index at any time by calling Add()
+// or Remove(). However when the index is modified, you must call Init() on
+// each iterator before using it again (or simply create a new iterator).
+//
+// index.Add(new_point, 123456);
+// it.Init(&index);
+// it.Seek(target.range_min());
+//
+// You can also access the index directly using the iterator interface. For
+// example, here is how to iterate through all the points in a given S2CellId
+// "target_id":
+//
+// S2PointIndex<int>::Iterator it(&index);
+// it.Seek(target_id.range_min());
+// for (; !it.done() && it.id() <= target_id.range_max(); it.Next()) {
+// DoSomething(it.id(), it.point(), it.data());
+// }
+//
+// TODO(ericv): Consider adding an S2PointIndexRegion class, which could be
+// used to efficiently compute coverings of a collection of S2Points.
+//
+// REQUIRES: "Data" has default and copy constructors.
+// REQUIRES: "Data" has operator== and operator<.
+template <class Data = std::tuple<> /*empty class*/>
+class S2PointIndex {
+ public:
+ // PointData is essentially std::pair with named fields. It stores an
+ // S2Point and its associated data, taking advantage of the "empty base
+ // optimization" to ensure that no extra space is used when Data is empty.
+ class PointData {
+ public:
+ PointData() {} // Needed by STL
+ PointData(const S2Point& point, const Data& data) : tuple_(point, data) {}
+
+ const S2Point& point() const { return std::get<0>(tuple_); }
+ const Data& data() const { return std::get<1>(tuple_); }
+
+ friend bool operator==(const PointData& x, const PointData& y) {
+ return x.tuple_ == y.tuple_;
+ }
+ friend bool operator<(const PointData& x, const PointData& y) {
+ return x.tuple_ < y.tuple_;
+ }
+
+ private:
+ // Note that std::tuple has special template magic to ensure that Data
+ // doesn't take up any space when it is empty. (This is not true if you
+ // simply declare a member of type Data.)
+ std::tuple<S2Point, Data> tuple_;
+ };
+
+ // Default constructor.
+ S2PointIndex();
+
+ // Returns the number of points in the index.
+ int num_points() const;
+
+ // Adds the given point to the index. Invalidates all iterators.
+ void Add(const S2Point& point, const Data& data);
+ void Add(const PointData& point_data);
+
+ // Convenience function for the case when Data is an empty class.
+ void Add(const S2Point& point);
+
+ // Removes the given point from the index. Both the "point" and "data"
+ // fields must match the point to be removed. Returns false if the given
+ // point was not present. Invalidates all iterators.
+ bool Remove(const S2Point& point, const Data& data);
+ bool Remove(const PointData& point_data);
+
+ // Convenience function for the case when Data is an empty class.
+ void Remove(const S2Point& point);
+
+ // Resets the index to its original empty state. Invalidates all iterators.
+ void Clear();
+
+ private:
+ // Defined here because the Iterator class below uses it.
+ using Map = gtl::btree_multimap<S2CellId, PointData>;
+
+ public:
+ class Iterator {
+ public:
+ // Default constructor; must be followed by a call to Init().
+ Iterator();
+
+ // Convenience constructor that calls Init().
+ explicit Iterator(const S2PointIndex* index);
+
+ // Initializes an iterator for the given S2PointIndex. If the index is
+ // non-empty, the iterator is positioned at the first cell.
+ //
+ // This method may be called multiple times, e.g. to make an iterator
+ // valid again after the index is modified.
+ void Init(const S2PointIndex* index);
+
+ // The S2CellId for the current index entry.
+ // REQUIRES: !done()
+ S2CellId id() const;
+
+ // The point associated with the current index entry.
+ // REQUIRES: !done()
+ const S2Point& point() const;
+
+ // The client-supplied data associated with the current index entry.
+ // REQUIRES: !done()
+ const Data& data() const;
+
+ // The (S2Point, data) pair associated with the current index entry.
+ const PointData& point_data() const;
+
+ // Returns true if the iterator is positioned past the last index entry.
+ bool done() const;
+
+ // Positions the iterator at the first index entry (if any).
+ void Begin();
+
+ // Positions the iterator so that done() is true.
+ void Finish();
+
+ // Advances the iterator to the next index entry.
+ // REQUIRES: !done()
+ void Next();
+
+ // If the iterator is already positioned at the beginning, returns false.
+ // Otherwise positions the iterator at the previous entry and returns true.
+ bool Prev();
+
+ // Positions the iterator at the first entry with id() >= target, or at the
+ // end of the index if no such entry exists.
+ void Seek(S2CellId target);
+
+ private:
+ const Map* map_;
+ typename Map::const_iterator iter_, end_;
+ };
+
+ private:
+ friend class Iterator;
+ Map map_;
+
+ S2PointIndex(const S2PointIndex&) = delete;
+ void operator=(const S2PointIndex&) = delete;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+template <class Data>
+S2PointIndex<Data>::S2PointIndex() {
+}
+
+template <class Data>
+inline int S2PointIndex<Data>::num_points() const {
+ return map_.size();
+}
+
+template <class Data>
+void S2PointIndex<Data>::Add(const PointData& point_data) {
+ S2CellId id(point_data.point());
+ map_.insert(std::make_pair(id, point_data));
+}
+
+template <class Data>
+void S2PointIndex<Data>::Add(const S2Point& point, const Data& data) {
+ Add(PointData(point, data));
+}
+
+template <class Data>
+void S2PointIndex<Data>::Add(const S2Point& point) {
+ static_assert(std::is_empty<Data>::value, "Data must be empty");
+ Add(point, {});
+}
+
+template <class Data>
+bool S2PointIndex<Data>::Remove(const PointData& point_data) {
+ S2CellId id(point_data.point());
+ for (typename Map::iterator it = map_.lower_bound(id), end = map_.end();
+ it != end && it->first == id; ++it) {
+ if (it->second == point_data) {
+ map_.erase(it);
+ return true;
+ }
+ }
+ return false;
+}
+
+template <class Data>
+bool S2PointIndex<Data>::Remove(const S2Point& point, const Data& data) {
+ return Remove(PointData(point, data));
+}
+
+template <class Data>
+void S2PointIndex<Data>::Remove(const S2Point& point) {
+ static_assert(std::is_empty<Data>::value, "Data must be empty");
+ Remove(point, {});
+}
+
+template <class Data>
+void S2PointIndex<Data>::Clear() {
+ map_.clear();
+}
+
+template <class Data>
+inline S2PointIndex<Data>::Iterator::Iterator() : map_(nullptr) {
+}
+
+template <class Data>
+inline S2PointIndex<Data>::Iterator::Iterator(
+ const S2PointIndex<Data>* index) {
+ Init(index);
+}
+
+template <class Data>
+inline void S2PointIndex<Data>::Iterator::Init(
+ const S2PointIndex<Data>* index) {
+ map_ = &index->map_;
+ iter_ = map_->begin();
+ end_ = map_->end();
+}
+
+template <class Data>
+inline S2CellId S2PointIndex<Data>::Iterator::id() const {
+ S2_DCHECK(!done());
+ return iter_->first;
+}
+
+template <class Data>
+inline const S2Point& S2PointIndex<Data>::Iterator::point() const {
+ S2_DCHECK(!done());
+ return iter_->second.point();
+}
+
+template <class Data>
+inline const Data& S2PointIndex<Data>::Iterator::data() const {
+ S2_DCHECK(!done());
+ return iter_->second.data();
+}
+
+template <class Data>
+inline const typename S2PointIndex<Data>::PointData&
+S2PointIndex<Data>::Iterator::point_data() const {
+ S2_DCHECK(!done());
+ return iter_->second;
+}
+
+template <class Data>
+inline bool S2PointIndex<Data>::Iterator::done() const {
+ return iter_ == end_;
+}
+
+template <class Data>
+inline void S2PointIndex<Data>::Iterator::Begin() {
+ iter_ = map_->begin();
+}
+
+template <class Data>
+inline void S2PointIndex<Data>::Iterator::Finish() {
+ iter_ = end_;
+}
+
+template <class Data>
+inline void S2PointIndex<Data>::Iterator::Next() {
+ S2_DCHECK(!done());
+ ++iter_;
+}
+
+template <class Data>
+inline bool S2PointIndex<Data>::Iterator::Prev() {
+ if (iter_ == map_->begin()) return false;
+ --iter_;
+ return true;
+}
+
+template <class Data>
+inline void S2PointIndex<Data>::Iterator::Seek(S2CellId target) {
+ iter_ = map_->lower_bound(target);
+}
+
+#endif // S2_S2POINT_INDEX_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2POINT_REGION_H_
+#define S2_S2POINT_REGION_H_
+
+#include "s2/base/logging.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/s1angle.h"
+#include "s2/s2pointutil.h"
+#include "s2/s2region.h"
+#include "s2/third_party/absl/base/macros.h"
+
+class Decoder;
+class Encoder;
+class S2Cap;
+class S2Cell;
+class S2LatLngRect;
+
+// An S2PointRegion is a region that contains a single point. It is more
+// expensive than the raw S2Point type and is useful mainly for completeness.
+//
+// This class is intended to be copied by value as desired. It uses
+// the default copy constructor and assignment operator.
+class S2PointRegion final : public S2Region {
+ public:
+ // Create a region containing the given point, which must be unit length.
+ explicit S2PointRegion(const S2Point& point);
+
+ ~S2PointRegion() override;
+
+ const S2Point& point() const { return point_; }
+
+ ////////////////////////////////////////////////////////////////////////
+ // S2Region interface (see s2region.h for details):
+
+ S2PointRegion* Clone() const override;
+ S2Cap GetCapBound() const override;
+ S2LatLngRect GetRectBound() const override;
+ bool Contains(const S2Cell& cell) const override { return false; }
+ bool MayIntersect(const S2Cell& cell) const override;
+ bool Contains(const S2Point& p) const override { return (point_ == p); }
+
+ // Appends a serialized representation of the S2Point to "encoder".
+ //
+ // REQUIRES: "encoder" uses the default constructor, so that its buffer
+ // can be enlarged as necessary by calling Ensure(int).
+ void Encode(Encoder* const encoder) const;
+
+ // Decodes an S2Point encoded with Encode(). Returns true on success.
+ // (Returns false if the encoded point is not unit length.)
+ bool Decode(Decoder* const decoder);
+
+ private:
+ S2Point point_;
+};
+
+inline S2PointRegion::S2PointRegion(const S2Point& point) : point_(point) {
+ S2_DCHECK(S2::IsUnitLength(point));
+}
+
+#endif // S2_S2POINT_REGION_H_
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2POINT_SPAN_H_
+#define S2_S2POINT_SPAN_H_
+
+#include "s2/base/logging.h"
+#include "s2/third_party/absl/types/span.h"
+#include "s2/s2point.h"
+
+// S2PointSpan represents a view of an S2Point array. It is used to pass
+// vertex arrays to functions that don't care about the actual array type
+// (e.g. std::vector<S2Point> or S2Point[]).
+//
+// NOTE: S2PointSpan has an implicit constructor from any container type with
+// data() and size() methods (such as std::vector and std::array). Therefore
+// you can use such containers as arguments for any S2PointSpan parameter.
+using S2PointSpan = absl::Span<const S2Point>;
+
+// Like S2PointSpan, except that operator[] maps index values in the range
+// [n, 2*n-1] to the range [0, n-1] by subtracting n (where n == size()).
+// In other words, two full copies of the vertex array are available. (This
+// is a compromise between convenience and efficiency, since computing the
+// index modulo "n" is surprisingly expensive.)
+//
+// This property is useful for implementing algorithms where the elements of
+// the span represent the vertices of a loop.
+class S2PointLoopSpan : public S2PointSpan {
+ public:
+ // Inherit all constructors.
+ using absl::Span<const S2Point>::Span;
+
+ // Like operator[], but allows index values in the range [0, 2*size()-1]
+ // where each index i >= size() is mapped to i - size().
+ reference operator[](int i) const noexcept {
+ S2_DCHECK_GE(i, 0);
+ S2_DCHECK_LT(i, 2 * size());
+ int j = i - static_cast<int>(size());
+ return S2PointSpan::operator[](j < 0 ? i : j);
+ }
+};
+
+#endif // S2_S2POINT_SPAN_H_
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2POINT_VECTOR_SHAPE_H_
+#define S2_S2POINT_VECTOR_SHAPE_H_
+
+#include <vector>
+#include "s2/encoded_s2point_vector.h"
+#include "s2/s2shape.h"
+
+// S2PointVectorShape is an S2Shape representing a set of S2Points. Each point
+// is reprsented as a degenerate edge with the same starting and ending
+// vertices.
+//
+// This class is useful for adding a collection of points to an S2ShapeIndex.
+class S2PointVectorShape : public S2Shape {
+ public:
+ static constexpr TypeTag kTypeTag = 3;
+
+ // Constructs an empty point vector.
+ S2PointVectorShape() {}
+
+ // Constructs an S2PointVectorShape from a vector of points.
+ explicit S2PointVectorShape(std::vector<S2Point> points) {
+ points_ = std::move(points);
+ }
+
+ ~S2PointVectorShape() override = default;
+
+ int num_points() const { return static_cast<int>(points_.size()); }
+ const S2Point& point(int i) const { return points_[i]; }
+
+ // Appends an encoded representation of the S2PointVectorShape to "encoder".
+ //
+ // REQUIRES: "encoder" uses the default constructor, so that its buffer
+ // can be enlarged as necessary by calling Ensure(int).
+ void Encode(Encoder* encoder,
+ s2coding::CodingHint hint = s2coding::CodingHint::COMPACT) const {
+ s2coding::EncodeS2PointVector(points_, hint, encoder);
+ }
+
+ // Decodes an S2PointVectorShape, returning true on success. (The method
+ // name is chosen for compatibility with EncodedS2PointVectorShape below.)
+ bool Init(Decoder* decoder) {
+ s2coding::EncodedS2PointVector points;
+ if (!points.Init(decoder)) return false;
+ points_ = points.Decode();
+ return true;
+ }
+
+ // S2Shape interface:
+ int num_edges() const final { return num_points(); }
+ Edge edge(int e) const final { return Edge(points_[e], points_[e]); }
+ int dimension() const final { return 0; }
+ ReferencePoint GetReferencePoint() const final {
+ return ReferencePoint::Contained(false);
+ }
+ int num_chains() const final { return num_points(); }
+ Chain chain(int i) const final { return Chain(i, 1); }
+ Edge chain_edge(int i, int j) const final {
+ S2_DCHECK_EQ(j, 0);
+ return Edge(points_[i], points_[i]);
+ }
+ ChainPosition chain_position(int e) const final {
+ return ChainPosition(e, 0);
+ }
+ TypeTag type_tag() const override { return kTypeTag; }
+
+ private:
+ std::vector<S2Point> points_;
+};
+
+// Exactly like S2PointVectorShape, except that the points are kept in an
+// encoded form and are decoded only as they are accessed. This allows for
+// very fast initialization and no additional memory use beyond the encoded
+// data. The encoded data is not owned by this class; typically it points
+// into a large contiguous buffer that contains other encoded data as well.
+class EncodedS2PointVectorShape : public S2Shape {
+ public:
+ // Constructs an uninitialized object; requires Init() to be called.
+ EncodedS2PointVectorShape() {}
+
+ // Initializes an EncodedS2PointVectorShape.
+ //
+ // REQUIRES: The Decoder data buffer must outlive this object.
+ bool Init(Decoder* decoder) { return points_.Init(decoder); }
+
+ int num_points() const { return static_cast<int>(points_.size()); }
+ S2Point point(int i) const { return points_[i]; }
+
+ // S2Shape interface:
+ int num_edges() const final { return num_points(); }
+ Edge edge(int e) const final { return Edge(points_[e], points_[e]); }
+ int dimension() const final { return 0; }
+ ReferencePoint GetReferencePoint() const final {
+ return ReferencePoint::Contained(false);
+ }
+ int num_chains() const final { return num_points(); }
+ Chain chain(int i) const final { return Chain(i, 1); }
+ Edge chain_edge(int i, int j) const final {
+ S2_DCHECK_EQ(j, 0);
+ return Edge(points_[i], points_[i]);
+ }
+ ChainPosition chain_position(int e) const final {
+ return ChainPosition(e, 0);
+ }
+
+ private:
+ s2coding::EncodedS2PointVector points_;
+};
+
+
+#endif // S2_S2POINT_VECTOR_SHAPE_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// Defines additional operations for points on the unit sphere (in addition to
+// the standard vector operations defined in "util/math/vector.h").
+
+#ifndef S2_S2POINTUTIL_H_
+#define S2_S2POINTUTIL_H_
+
+#include "s2/_fp_contract_off.h"
+#include "s2/s1angle.h"
+#include "s2/s2point.h"
+#include "s2/util/math/matrix3x3.h"
+
+// S2 is a namespace for constants and simple utility functions that are used
+// throughout the S2 library. The name "S2" is derived from the mathematical
+// symbol for the two-dimensional unit sphere (note that the "2" refers to the
+// dimension of the surface, not the space it is embedded in).
+namespace S2 {
+
+// Return a unique "origin" on the sphere for operations that need a fixed
+// reference point. In particular, this is the "point at infinity" used for
+// point-in-polygon testing (by counting the number of edge crossings).
+inline S2Point Origin();
+
+// Return true if the given point is approximately unit length
+// (this is mainly useful for assertions).
+bool IsUnitLength(const S2Point& p);
+
+// Return true if two points are within the given distance of each other (this
+// is mainly useful for testing). It is an error if either point is a
+// zero-length vector (default S2Point), but this is only checked in debug mode.
+// In non-debug mode it will always return true.
+bool ApproxEquals(const S2Point& a, const S2Point& b,
+ S1Angle max_error = S1Angle::Radians(1e-15));
+
+// Return a unit-length vector that is orthogonal to "a". Satisfies
+// Ortho(-a) = -Ortho(a) for all a.
+//
+// Note that Vector3_d also defines an "Ortho" method, but this one is
+// preferred for use in S2 code because it explicitly tries to avoid result
+// result coordinates that are zero. (This is a performance optimization that
+// reduces the amount of time spent in functions which handle degeneracies.)
+S2Point Ortho(const S2Point& a);
+
+// Return a vector "c" that is orthogonal to the given unit-length vectors
+// "a" and "b". This function is similar to a.CrossProd(b) except that it
+// does a better job of ensuring orthogonality when "a" is nearly parallel
+// to "b", and it returns a non-zero result even when a == b or a == -b.
+//
+// It satisfies the following properties (RCP == RobustCrossProd):
+//
+// (1) RCP(a,b) != 0 for all a, b
+// (2) RCP(b,a) == -RCP(a,b) unless a == b or a == -b
+// (3) RCP(-a,b) == -RCP(a,b) unless a == b or a == -b
+// (4) RCP(a,-b) == -RCP(a,b) unless a == b or a == -b
+//
+// The result is not guaranteed to be unit length.
+S2Point RobustCrossProd(const S2Point& a, const S2Point& b);
+
+// Rotate the given point about the given axis by the given angle. "p" and
+// "axis" must be unit length; "angle" has no restrictions (e.g., it can be
+// positive, negative, greater than 360 degrees, etc).
+S2Point Rotate(const S2Point& p, const S2Point& axis, S1Angle angle);
+
+// Extend the given point "z" on the unit sphere into a right-handed
+// coordinate frame of unit-length column vectors m = (x,y,z). Note that the
+// vectors (x,y) are an orthonormal frame for the tangent space at "z", while
+// "z" itself is an orthonormal frame for the normal space at "z".
+Matrix3x3_d GetFrame(const S2Point& z);
+void GetFrame(const S2Point& z, Matrix3x3_d* m);
+
+// Given an orthonormal basis "m" of column vectors and a point "p", return
+// the coordinates of "p" with respect to the basis "m". The resulting
+// point "q" satisfies the identity (m * q == p).
+S2Point ToFrame(const Matrix3x3_d& m, const S2Point& p);
+
+// Given an orthonormal basis "m" of column vectors and a point "q" with
+// respect to that basis, return the equivalent point "p" with respect to
+// the standard axis-aligned basis. The result satisfies (p == m * q).
+Matrix3x3_d GetFrame(const S2Point& z);
+S2Point FromFrame(const Matrix3x3_d& m, const S2Point& q);
+
+// Return true if the points A, B, C are strictly counterclockwise. Return
+// false if the points are clockwise or collinear (i.e. if they are all
+// contained on some great circle).
+//
+// Due to numerical errors, situations may arise that are mathematically
+// impossible, e.g. ABC may be considered strictly CCW while BCA is not.
+// However, the implementation guarantees the following:
+//
+// If SimpleCCW(a,b,c), then !SimpleCCW(c,b,a) for all a,b,c.
+ABSL_DEPRECATED("Use s2pred::Sign instead.")
+bool SimpleCCW(const S2Point& a, const S2Point& b, const S2Point& c);
+
+
+////////////////// Implementation details follow ////////////////////
+
+// Uncomment the following line for testing purposes only.
+// #define S2_TEST_DEGENERACIES
+
+inline S2Point Origin() {
+#ifdef S2_TEST_DEGENERACIES
+ // This value makes polygon operations much slower, because it greatly
+ // increases the number of degenerate cases that need to be handled using
+ // s2pred::ExpensiveSign().
+ return S2Point(0, 0, 1);
+#else
+ // The origin should not be a point that is commonly used in edge tests in
+ // order to avoid triggering code to handle degenerate cases. (This rules
+ // out the north and south poles.) It should also not be on the boundary of
+ // any low-level S2Cell for the same reason.
+ //
+ // The point chosen here is about 66km from the north pole towards the East
+ // Siberian Sea. See the unittest for more details. It is written out
+ // explicitly using floating-point literals because the optimizer doesn't
+ // seem willing to evaluate Normalize() at compile time.
+ return S2Point(-0.0099994664350250197, 0.0025924542609324121,
+ 0.99994664350250195);
+#endif
+}
+
+} // namespace S2
+
+#endif // S2_S2POINTUTIL_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2POLYGON_H_
+#define S2_S2POLYGON_H_
+
+#include <atomic>
+#include <cstddef>
+#include <map>
+#include <vector>
+
+#include "s2/base/integral_types.h"
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/mutable_s2shape_index.h"
+#include "s2/s1angle.h"
+#include "s2/s2boolean_operation.h"
+#include "s2/s2builder.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2debug.h"
+#include "s2/s2latlng_rect.h"
+#include "s2/s2loop.h"
+#include "s2/s2polyline.h"
+#include "s2/s2region.h"
+#include "s2/s2shape_index.h"
+
+class Decoder;
+class Encoder;
+class S1Angle;
+class S2Cap;
+class S2Cell;
+class S2CellUnion;
+class S2Error;
+class S2Loop;
+class S2PolygonBuilder;
+class S2Polyline;
+struct S2XYZFaceSiTi;
+
+// An S2Polygon is an S2Region object that represents a polygon. A polygon is
+// defined by zero or more loops; recall that the interior of a loop is
+// defined to be its left-hand side (see S2Loop). There are two different
+// conventions for creating an S2Polygon:
+//
+// - InitNested() expects the input loops to be nested hierarchically. The
+// polygon interior then consists of the set of points contained by an odd
+// number of loops. So for example, a circular region with a hole in it
+// would be defined as two CCW loops, with one loop containing the other.
+// The loops can be provided in any order.
+//
+// When the orientation of the input loops is unknown, the nesting
+// requirement is typically met by calling S2Loop::Normalize() on each
+// loop (which inverts the loop if necessary so that it encloses at most
+// half the sphere). But in fact any set of loops can be used as long as
+// (1) there is no pair of loops that cross, and (2) there is no pair of
+// loops whose union is the entire sphere.
+//
+// - InitOriented() expects the input loops to be oriented such that the
+// polygon interior is on the left-hand side of every loop. So for
+// example, a circular region with a hole in it would be defined using a
+// CCW outer loop and a CW inner loop. The loop orientations must all be
+// consistent; for example, it is not valid to have one CCW loop nested
+// inside another CCW loop, because the region between the two loops is on
+// the left-hand side of one loop and the right-hand side of the other.
+//
+// Most clients will not call these methods directly; instead they should use
+// S2Builder, which has better support for dealing with imperfect data.
+//
+// When the polygon is initialized, the given loops are automatically
+// converted into a canonical form consisting of "shells" and "holes". Shells
+// and holes are both oriented CCW, and are nested hierarchically. The loops
+// are reordered to correspond to a preorder traversal of the nesting
+// hierarchy; InitOriented may also invert some loops. The set of input S2Loop
+// pointers is always preserved; the caller can use this to determine how the
+// loops were reordered if desired.
+//
+// Polygons may represent any region of the sphere with a polygonal boundary,
+// including the entire sphere (known as the "full" polygon). The full
+// polygon consists of a single full loop (see S2Loop), whereas the empty
+// polygon has no loops at all.
+//
+// Polygons have the following restrictions:
+//
+// - Loops may not cross, i.e. the boundary of a loop may not intersect
+// both the interior and exterior of any other loop.
+//
+// - Loops may not share edges, i.e. if a loop contains an edge AB, then
+// no other loop may contain AB or BA.
+//
+// - Loops may share vertices, however no vertex may appear twice in a
+// single loop (see S2Loop).
+//
+// - No loop may be empty. The full loop may appear only in the full polygon.
+
+class S2Polygon final : public S2Region {
+ public:
+ // The default constructor creates an empty polygon. It can be made
+ // non-empty by calling Init(), Decode(), etc.
+ S2Polygon();
+
+ // Hide these overloads from SWIG to prevent compilation errors.
+ // TODO(user): Fix the SWIG wrapping in a better way.
+#ifndef SWIG
+ // Convenience constructor that calls InitNested() with the given loops.
+ //
+ // When called with override == S2Debug::ALLOW, the automatic validity
+ // checking is controlled by --s2debug (which is true by default in
+ // non-optimized builds). When this flag is enabled, a fatal error is
+ // generated whenever an invalid polygon is constructed.
+ //
+ // With override == S2Debug::DISABLE, the automatic validity checking
+ // is disabled. The main reason to do this is if you intend to call
+ // IsValid() explicitly. (See set_s2debug_override() for details.)
+ // Example:
+ //
+ // std::vector<std::unique_ptr<S2Loop>> loops;
+ // // ... set up loops ...
+ // S2Polygon* polygon = new S2Polygon(std::move(loops), S2Debug::DISABLE);
+ //
+ // This is equivalent to:
+ //
+ // S2Polygon* polygon = new S2Polygon;
+ // polygon->set_s2debug_override(S2Debug::DISABLE);
+ // polygon->InitNested(std::move(loops));
+ explicit S2Polygon(std::vector<std::unique_ptr<S2Loop> > loops,
+ S2Debug override = S2Debug::ALLOW);
+#endif
+
+ // Convenience constructor that creates a polygon with a single loop
+ // corresponding to the given cell.
+ explicit S2Polygon(const S2Cell& cell);
+
+#ifndef SWIG
+ // Convenience constructor that calls Init(S2Loop*). Note that this method
+ // automatically converts the special empty loop (see S2Loop) into an empty
+ // polygon, unlike the vector-of-loops constructor which does not allow
+ // empty loops at all.
+ explicit S2Polygon(std::unique_ptr<S2Loop> loop,
+ S2Debug override = S2Debug::ALLOW);
+
+ // Create a polygon from a set of hierarchically nested loops. The polygon
+ // interior consists of the points contained by an odd number of loops.
+ // (Recall that a loop contains the set of points on its left-hand side.)
+ //
+ // This method figures out the loop nesting hierarchy and assigns every
+ // loop a depth. Shells have even depths, and holes have odd depths. Note
+ // that the loops are reordered so the hierarchy can be traversed more
+ // easily (see GetParent(), GetLastDescendant(), and S2Loop::depth()).
+ //
+ // This method may be called more than once, in which case any existing
+ // loops are deleted before being replaced by the input loops.
+ void InitNested(std::vector<std::unique_ptr<S2Loop> > loops);
+
+ // Like InitNested(), but expects loops to be oriented such that the polygon
+ // interior is on the left-hand side of all loops. This implies that shells
+ // and holes should have opposite orientations in the input to this method.
+ // (During initialization, loops representing holes will automatically be
+ // inverted.)
+ void InitOriented(std::vector<std::unique_ptr<S2Loop> > loops);
+
+ // Initialize a polygon from a single loop. Note that this method
+ // automatically converts the special empty loop (see S2Loop) into an empty
+ // polygon, unlike the vector-of-loops InitNested() method which does not
+ // allow empty loops at all.
+ void Init(std::unique_ptr<S2Loop> loop);
+#endif // !defined(SWIG)
+
+ // Releases ownership of and returns the loops of this polygon, and resets
+ // the polygon to be empty.
+ std::vector<std::unique_ptr<S2Loop> > Release();
+
+ // Makes a deep copy of the given source polygon. The destination polygon
+ // will be cleared if necessary.
+ void Copy(const S2Polygon* src);
+
+ // Destroys the polygon and frees its loops.
+ ~S2Polygon() override;
+
+ // Allows overriding the automatic validity checks controlled by
+ // --s2debug (which is true by default in non-optimized builds).
+ // When this flag is enabled, a fatal error is generated whenever
+ // an invalid polygon is constructed. The main reason to disable
+ // this flag is if you intend to call IsValid() explicitly, like this:
+ //
+ // S2Polygon polygon;
+ // polygon.set_s2debug_override(S2Debug::DISABLE);
+ // polygon.Init(...);
+ // if (!polygon.IsValid()) { ... }
+ //
+ // This setting is preserved across calls to Init() and Decode().
+ void set_s2debug_override(S2Debug override);
+ S2Debug s2debug_override() const;
+
+ // Returns true if this is a valid polygon (including checking whether all
+ // the loops are themselves valid). Note that validity is checked
+ // automatically during initialization when --s2debug is enabled (true by
+ // default in debug binaries).
+ bool IsValid() const;
+
+ // Returns true if this is *not* a valid polygon and sets "error"
+ // appropriately. Otherwise returns false and leaves "error" unchanged.
+ //
+ // Note that in error messages, loops that represent holes have their edges
+ // numbered in reverse order, starting from the last vertex of the loop.
+ //
+ // REQUIRES: error != nullptr
+ bool FindValidationError(S2Error* error) const;
+
+ // Return true if this is the empty polygon (consisting of no loops).
+ bool is_empty() const { return loops_.empty(); }
+
+ // Return true if this is the full polygon (consisting of a single loop that
+ // encompasses the entire sphere).
+ bool is_full() const { return num_loops() == 1 && loop(0)->is_full(); }
+
+ // Return the number of loops in this polygon.
+ int num_loops() const { return static_cast<int>(loops_.size()); }
+
+ // Total number of vertices in all loops.
+ int num_vertices() const { return num_vertices_; }
+
+ // Return the loop at the given index. Note that during initialization, the
+ // given loops are reordered according to a preorder traversal of the loop
+ // nesting hierarchy. This implies that every loop is immediately followed
+ // by its descendants. This hierarchy can be traversed using the methods
+ // GetParent(), GetLastDescendant(), and S2Loop::depth().
+ const S2Loop* loop(int k) const { return loops_[k].get(); }
+ S2Loop* loop(int k) { return loops_[k].get(); }
+
+ // Return the index of the parent of loop k, or -1 if it has no parent.
+ int GetParent(int k) const;
+
+ // Return the index of the last loop that is contained within loop k.
+ // Returns num_loops() - 1 if k < 0. Note that loops are indexed according
+ // to a preorder traversal of the nesting hierarchy, so the immediate
+ // children of loop k can be found by iterating over loops
+ // (k+1)..GetLastDescendant(k) and selecting those whose depth is equal to
+ // (loop(k)->depth() + 1).
+ int GetLastDescendant(int k) const;
+
+ // Return the area of the polygon interior, i.e. the region on the left side
+ // of an odd number of loops. The return value is between 0 and 4*Pi.
+ double GetArea() const;
+
+ // Return the true centroid of the polygon multiplied by the area of the
+ // polygon (see s2centroids.h for details on centroids). The result is not
+ // unit length, so you may want to normalize it. Also note that in general,
+ // the centroid may not be contained by the polygon.
+ //
+ // We prescale by the polygon area for two reasons: (1) it is cheaper to
+ // compute this way, and (2) it makes it easier to compute the centroid of
+ // more complicated shapes (by splitting them into disjoint regions and
+ // adding their centroids).
+ S2Point GetCentroid() const;
+
+ // If all of the polygon's vertices happen to be the centers of S2Cells at
+ // some level, then return that level, otherwise return -1. See also
+ // InitToSnapped() and s2builderutil::S2CellIdSnapFunction.
+ // Returns -1 if the polygon has no vertices.
+ int GetSnapLevel() const;
+
+ // Return the distance from the given point to the polygon interior. If the
+ // polygon is empty, return S1Angle::Infinity(). "x" should be unit length.
+ S1Angle GetDistance(const S2Point& x) const;
+
+ // Return the distance from the given point to the polygon boundary. If the
+ // polygon is empty or full, return S1Angle::Infinity() (since the polygon
+ // has no boundary). "x" should be unit length.
+ S1Angle GetDistanceToBoundary(const S2Point& x) const;
+
+ // Return the overlap fractions between two polygons, i.e. the ratios of the
+ // area of intersection to the area of each polygon.
+ static std::pair<double, double> GetOverlapFractions(const S2Polygon* a,
+ const S2Polygon* b);
+
+ // If the given point is contained by the polygon, return it. Otherwise
+ // return the closest point on the polygon boundary. If the polygon is
+ // empty, return the input argument. Note that the result may or may not be
+ // contained by the polygon. "x" should be unit length.
+ S2Point Project(const S2Point& x) const;
+
+ // Return the closest point on the polygon boundary to the given point. If
+ // the polygon is empty or full, return the input argument (since the
+ // polygon has no boundary). "x" should be unit length.
+ S2Point ProjectToBoundary(const S2Point& x) const;
+
+ // Return true if this polygon contains the given other polygon, i.e.
+ // if polygon A contains all points contained by polygon B.
+ bool Contains(const S2Polygon* b) const;
+
+ // Returns true if this polgyon (A) approximately contains the given other
+ // polygon (B). This is true if it is possible to move the vertices of B
+ // no further than "tolerance" such that A contains the modified B.
+ //
+ // For example, the empty polygon will contain any polygon whose maximum
+ // width is no more than "tolerance".
+ bool ApproxContains(const S2Polygon* b, S1Angle tolerance) const;
+
+ // Return true if this polygon intersects the given other polygon, i.e.
+ // if there is a point that is contained by both polygons.
+ bool Intersects(const S2Polygon* b) const;
+
+ // Returns true if this polgyon (A) and the given polygon (B) are
+ // approximately disjoint. This is true if it is possible to ensure that A
+ // and B do not intersect by moving their vertices no further than
+ // "tolerance". This implies that in borderline cases where A and B overlap
+ // slightly, this method returns true (A and B are approximately disjoint).
+ //
+ // For example, any polygon is approximately disjoint from a polygon whose
+ // maximum width is no more than "tolerance".
+ bool ApproxDisjoint(const S2Polygon* b, S1Angle tolerance) const;
+
+ // Initialize this polygon to the intersection, union, difference (A - B),
+ // or symmetric difference (XOR) of the given two polygons.
+ //
+ // "snap_function" allows you to specify a minimum spacing between output
+ // vertices, and/or that the vertices should be snapped to a discrete set of
+ // points (e.g. S2CellId centers or E7 lat/lng coordinates). Any snap
+ // function can be used, including the IdentitySnapFunction with a
+ // snap_radius of zero (which preserves the input vertices exactly).
+ //
+ // The boundary of the output polygon before snapping is guaranteed to be
+ // accurate to within S2::kIntersectionError of the exact result.
+ // Snapping can move the boundary by an additional distance that depends on
+ // the snap function. Finally, any degenerate portions of the output
+ // polygon are automatically removed (i.e., regions that do not contain any
+ // points) since S2Polygon does not allow such regions.
+ //
+ // See S2Builder and s2builderutil for more details on snap functions. For
+ // example, you can snap to E7 coordinates by setting "snap_function" to
+ // s2builderutil::IntLatLngSnapFunction(7).
+ //
+ // The default snap function is the IdentitySnapFunction with a snap radius
+ // of S2::kIntersectionMergeRadius (equal to about 1.8e-15 radians
+ // or 11 nanometers on the Earth's surface). This means that vertices may
+ // be positioned arbitrarily, but vertices that are extremely close together
+ // can be merged together. The reason for a non-zero default snap radius is
+ // that it helps to eliminate narrow cracks and slivers when T-vertices are
+ // present. For example, adjacent S2Cells at different levels do not share
+ // exactly the same boundary, so there can be a narrow crack between them.
+ // If a polygon is intersected with those cells and the pieces are unioned
+ // together, the result would have a narrow crack unless the snap radius is
+ // set to a non-zero value.
+ //
+ // Note that if you want to encode the vertices in a lower-precision
+ // representation (such as S2CellIds or E7), it is much better to use a
+ // suitable SnapFunction rather than rounding the vertices yourself, because
+ // this will create self-intersections unless you ensure that the vertices
+ // and edges are sufficiently well-separated first. In particular you need
+ // to use a snap function whose min_edge_vertex_separation() is at least
+ // twice the maximum distance that a vertex can move when rounded.
+ //
+ // The versions of these functions with an S2Error argument return true on
+ // success and set "error" appropriately otherwise. However note that these
+ // functions should never return an error provided that both input polygons
+ // are valid (i.e., IsValid() returns true).
+ void InitToIntersection(const S2Polygon* a, const S2Polygon* b);
+ void InitToIntersection(const S2Polygon& a, const S2Polygon& b,
+ const S2Builder::SnapFunction& snap_function);
+ bool InitToIntersection(const S2Polygon& a, const S2Polygon& b,
+ const S2Builder::SnapFunction& snap_function,
+ S2Error *error);
+
+ void InitToUnion(const S2Polygon* a, const S2Polygon* b);
+ void InitToUnion(const S2Polygon& a, const S2Polygon& b,
+ const S2Builder::SnapFunction& snap_function);
+ bool InitToUnion(const S2Polygon& a, const S2Polygon& b,
+ const S2Builder::SnapFunction& snap_function,
+ S2Error *error);
+
+ void InitToDifference(const S2Polygon* a, const S2Polygon* b);
+ void InitToDifference(const S2Polygon& a, const S2Polygon& b,
+ const S2Builder::SnapFunction& snap_function);
+ bool InitToDifference(const S2Polygon& a, const S2Polygon& b,
+ const S2Builder::SnapFunction& snap_function,
+ S2Error *error);
+
+ void InitToSymmetricDifference(const S2Polygon* a, const S2Polygon* b);
+ void InitToSymmetricDifference(const S2Polygon& a, const S2Polygon& b,
+ const S2Builder::SnapFunction& snap_function);
+ bool InitToSymmetricDifference(const S2Polygon& a, const S2Polygon& b,
+ const S2Builder::SnapFunction& snap_function,
+ S2Error *error);
+
+ // Convenience functions that use the IdentitySnapFunction with the given
+ // snap radius. TODO(ericv): Consider deprecating these and require the
+ // snap function to be specified explcitly?
+ void InitToApproxIntersection(const S2Polygon* a, const S2Polygon* b,
+ S1Angle snap_radius);
+ void InitToApproxUnion(const S2Polygon* a, const S2Polygon* b,
+ S1Angle snap_radius);
+ void InitToApproxDifference(const S2Polygon* a, const S2Polygon* b,
+ S1Angle snap_radius);
+ void InitToApproxSymmetricDifference(const S2Polygon* a, const S2Polygon* b,
+ S1Angle snap_radius);
+
+ // Snaps the vertices of the given polygon using the given SnapFunction
+ // (e.g., s2builderutil::IntLatLngSnapFunction(6) snaps to E6 coordinates).
+ // This can change the polygon topology (merging loops, for example), but
+ // the resulting polygon is guaranteed to be valid, and no vertex will move
+ // by more than snap_function.snap_radius(). See S2Builder for other
+ // guarantees (e.g., minimum edge-vertex separation).
+ //
+ // Note that this method is a thin wrapper over S2Builder, so if you are
+ // starting with data that is not in S2Polygon format (e.g., integer E7
+ // coordinates) then it is faster to just use S2Builder directly.
+ void InitToSnapped(const S2Polygon& polygon,
+ const S2Builder::SnapFunction& snap_function);
+
+ // Convenience function that snaps the vertices to S2CellId centers at the
+ // given level (default level 30, which has S2CellId centers spaced about 1
+ // centimeter apart). Polygons can be efficiently encoded by Encode() after
+ // they have been snapped.
+ void InitToSnapped(const S2Polygon* polygon,
+ int snap_level = S2CellId::kMaxLevel);
+
+ // Snaps the input polygon according to the given "snap_function" and
+ // reduces the number of vertices if possible, while ensuring that no vertex
+ // moves further than snap_function.snap_radius().
+ //
+ // Simplification works by replacing nearly straight chains of short edges
+ // with longer edges, in a way that preserves the topology of the input
+ // polygon up to the creation of degeneracies. This means that loops or
+ // portions of loops may become degenerate, in which case they are removed.
+ // For example, if there is a very small island in the original polygon, it
+ // may disappear completely. (Even if there are dense islands, they could
+ // all be removed rather than being replaced by a larger simplified island
+ // if more area is covered by water than land.)
+ void InitToSimplified(const S2Polygon& a,
+ const S2Builder::SnapFunction& snap_function);
+
+ // Like InitToSimplified, except that any vertices or edges on the boundary
+ // of the given S2Cell are preserved if possible. This method requires that
+ // the polygon has already been clipped so that it does not extend outside
+ // the cell by more than "boundary_tolerance". In other words, it operates
+ // on polygons that have already been intersected with a cell.
+ //
+ // Typically this method is used in geometry-processing pipelines that
+ // intersect polygons with a collection of S2Cells and then process those
+ // cells in parallel, where each cell generates some geometry that needs to
+ // be simplified. In contrast, if you just need to simplify the *input*
+ // geometry then it is easier and faster to do the simplification before
+ // computing the intersection with any S2Cells.
+ //
+ // "boundary_tolerance" specifies how close a vertex must be to the cell
+ // boundary to be kept. The default tolerance is large enough to handle any
+ // reasonable way of interpolating points along the cell boundary, such as
+ // S2::GetIntersection(), S2::Interpolate(), or direct (u,v)
+ // interpolation using S2::FaceUVtoXYZ(). However, if the vertices have
+ // been snapped to a lower-precision representation (e.g., S2CellId centers
+ // or E7 coordinates) then you will need to set this tolerance explicitly.
+ // For example, if the vertices were snapped to E7 coordinates then
+ // "boundary_tolerance" should be set to
+ //
+ // s2builderutil::IntLatLngSnapFunction::MinSnapRadiusForExponent(7)
+ //
+ // Degenerate portions of loops are always removed, so if a vertex on the
+ // cell boundary belongs only to degenerate regions then it will not be
+ // kept. For example, if the input polygon is a narrow strip of width less
+ // than "snap_radius" along one side of the cell, then the entire loop may
+ // become degenerate and be removed.
+ //
+ // REQUIRES: all vertices of "a" are within "boundary_tolerance" of "cell".
+ void InitToSimplifiedInCell(
+ const S2Polygon* a, const S2Cell& cell, S1Angle snap_radius,
+ S1Angle boundary_tolerance = S1Angle::Radians(1e-15));
+
+ // Initialize this polygon to the complement of the given polygon.
+ void InitToComplement(const S2Polygon* a);
+
+ // Invert the polygon (replace it by its complement).
+ void Invert();
+
+ // Return true if this polygon contains the given polyline. This method
+ // returns an exact result, according to the following model:
+ //
+ // - All edges are geodesics (of course).
+ //
+ // - Vertices are ignored for the purposes of defining containment.
+ // (This is because polygons often do not contain their vertices, in
+ // order to that when a set of polygons tiles the sphere then every point
+ // is contained by exactly one polygon.)
+ //
+ // - Points that lie exactly on geodesic edges are resolved using symbolic
+ // perturbations (i.e., they are considered to be infinitesmally offset
+ // from the edge).
+ //
+ // - If the polygon and polyline share an edge, it is handled as follows.
+ // First, the polygon edges are oriented so that the interior is always
+ // on the left. Then the shared polyline edge is contained if and only
+ // if it is in the same direction as the corresponding polygon edge.
+ // (This model ensures that when a polyline is intersected with a polygon
+ // and its complement, the edge only appears in one of the two results.)
+ //
+ // TODO(ericv): Update the implementation to correspond to the model above.
+ bool Contains(const S2Polyline& b) const;
+
+ // Returns true if this polgyon approximately contains the given polyline
+ // This is true if it is possible to move the polyline vertices no further
+ // than "tolerance" such that the polyline is now contained.
+ bool ApproxContains(const S2Polyline& b, S1Angle tolerance) const;
+
+ // Return true if this polygon intersects the given polyline. This method
+ // returns an exact result; see Contains(S2Polyline) for details.
+ bool Intersects(const S2Polyline& b) const;
+
+ // Returns true if this polgyon is approximately disjoint from the given
+ // polyline. This is true if it is possible to avoid intersection by moving
+ // their vertices no further than "tolerance".
+ //
+ // This implies that in borderline cases where there is a small overlap,
+ // this method returns true (i.e., they are approximately disjoint).
+ bool ApproxDisjoint(const S2Polyline& b, S1Angle tolerance) const;
+
+#ifndef SWIG
+ // Intersect this polygon with the polyline "in" and return the resulting
+ // zero or more polylines. The polylines are returned in the order they
+ // would be encountered by traversing "in" from beginning to end.
+ // Note that the output may include polylines with only one vertex,
+ // but there will not be any zero-vertex polylines.
+ //
+ // This is equivalent to calling ApproxIntersectWithPolyline() with the
+ // "snap_radius" set to S2::kIntersectionMergeRadius.
+ std::vector<std::unique_ptr<S2Polyline> > IntersectWithPolyline(
+ const S2Polyline& in) const;
+
+ // Similar to IntersectWithPolyline(), except that vertices will be
+ // dropped as necessary to ensure that all adjacent vertices in the
+ // sequence obtained by concatenating the output polylines will be
+ // farther than "snap_radius" apart. Note that this can change
+ // the number of output polylines and/or yield single-vertex polylines.
+ std::vector<std::unique_ptr<S2Polyline> > ApproxIntersectWithPolyline(
+ const S2Polyline& in, S1Angle snap_radius) const;
+
+ // TODO(ericv): Update documentation.
+ std::vector<std::unique_ptr<S2Polyline>> IntersectWithPolyline(
+ const S2Polyline& in, const S2Builder::SnapFunction& snap_function) const;
+
+ // Same as IntersectWithPolyline, but subtracts this polygon from
+ // the given polyline.
+ std::vector<std::unique_ptr<S2Polyline> > SubtractFromPolyline(
+ const S2Polyline& in) const;
+
+ // Same as ApproxIntersectWithPolyline, but subtracts this polygon
+ // from the given polyline.
+ std::vector<std::unique_ptr<S2Polyline> > ApproxSubtractFromPolyline(
+ const S2Polyline& in, S1Angle snap_radius) const;
+
+ std::vector<std::unique_ptr<S2Polyline>> SubtractFromPolyline(
+ const S2Polyline& in, const S2Builder::SnapFunction& snap_function) const;
+
+ // Return a polygon which is the union of the given polygons.
+ static std::unique_ptr<S2Polygon> DestructiveUnion(
+ std::vector<std::unique_ptr<S2Polygon> > polygons);
+ static std::unique_ptr<S2Polygon> DestructiveApproxUnion(
+ std::vector<std::unique_ptr<S2Polygon> > polygons,
+ S1Angle snap_radius);
+#endif // !defined(SWIG)
+
+ // Initialize this polygon to the outline of the given cell union.
+ // In principle this polygon should exactly contain the cell union and
+ // this polygon's inverse should not intersect the cell union, but rounding
+ // issues may cause this not to be the case.
+ void InitToCellUnionBorder(const S2CellUnion& cells);
+
+ // Return true if every loop of this polygon shares at most one vertex with
+ // its parent loop. Every polygon has a unique normalized form. A polygon
+ // can be normalized by passing it through S2Builder (with no snapping) in
+ // order to reconstruct the polygon from its edges.
+ //
+ // Generally there is no reason to convert polygons to normalized form. It
+ // is mainly useful for testing in order to compare whether two polygons
+ // have exactly the same interior, even when they have a different loop
+ // structure. For example, a diamond nested within a square (touching at
+ // four points) could be represented as a square with a diamond-shaped hole,
+ // or as four triangles. Methods such as BoundaryApproxEquals() will report
+ // these polygons as being different (because they have different
+ // boundaries) even though they contain the same points. However if they
+ // are both converted to normalized form (the "four triangles" version) then
+ // they can be compared more easily.
+ //
+ // Also see ApproxEquals(), which can determine whether two polygons contain
+ // approximately the same set of points without any need for normalization.
+ bool IsNormalized() const;
+
+ // Return true if two polygons have exactly the same loops. The loops must
+ // appear in the same order, and corresponding loops must have the same
+ // linear vertex ordering (i.e., cyclic rotations are not allowed).
+ bool Equals(const S2Polygon* b) const;
+
+ // Return true if two polygons are approximately equal to within the given
+ // tolerance. This is true if it is possible to move the vertices of the
+ // two polygons so that they contain the same set of points.
+ //
+ // Note that according to this model, small regions less than "tolerance" in
+ // width do not need to be considered, since these regions can be collapsed
+ // into degenerate loops (which contain no points) by moving their vertices.
+ //
+ // This model is not as strict as using the Hausdorff distance would be, and
+ // it is also not as strict as BoundaryNear (defined below). However, it is
+ // a good choice for comparing polygons that have been snapped, simplified,
+ // unioned, etc, since these operations use a model similar to this one
+ // (i.e., degenerate loops or portions of loops are automatically removed).
+ bool ApproxEquals(const S2Polygon* b, S1Angle tolerance) const;
+
+ // Returns true if two polygons have the same boundary. More precisely,
+ // this method requires that both polygons have loops with the same cyclic
+ // vertex order and the same nesting hierarchy. (This implies that vertices
+ // may be cyclically rotated between corresponding loops, and the loop
+ // ordering may be different between the two polygons as long as the nesting
+ // hierarchy is the same.)
+ bool BoundaryEquals(const S2Polygon* b) const;
+
+ // Return true if two polygons have the same boundary except for vertex
+ // perturbations. Both polygons must have loops with the same cyclic vertex
+ // order and the same nesting hierarchy, but the vertex locations are
+ // allowed to differ by up to "max_error".
+ bool BoundaryApproxEquals(const S2Polygon& b,
+ S1Angle max_error = S1Angle::Radians(1e-15)) const;
+
+ // Return true if two polygons have boundaries that are within "max_error"
+ // of each other along their entire lengths. More precisely, there must be
+ // a bijection between the two sets of loops such that for each pair of
+ // loops, "a_loop->BoundaryNear(b_loop)" is true.
+ bool BoundaryNear(const S2Polygon& b,
+ S1Angle max_error = S1Angle::Radians(1e-15)) const;
+
+ // Returns the total number of bytes used by the polygon.
+ size_t SpaceUsed() const;
+
+ ////////////////////////////////////////////////////////////////////////
+ // S2Region interface (see s2region.h for details):
+
+ // GetRectBound() returns essentially tight results, while GetCapBound()
+ // might have a lot of extra padding. Both bounds are conservative in that
+ // if the loop contains a point P, then the bound contains P also.
+ S2Polygon* Clone() const override;
+ S2Cap GetCapBound() const override; // Cap surrounding rect bound.
+ S2LatLngRect GetRectBound() const override { return bound_; }
+ void GetCellUnionBound(std::vector<S2CellId> *cell_ids) const override;
+
+ bool Contains(const S2Cell& cell) const override;
+ bool MayIntersect(const S2Cell& cell) const override;
+
+ // The point 'p' does not need to be normalized.
+ bool Contains(const S2Point& p) const override;
+
+ // Appends a serialized representation of the S2Polygon to "encoder".
+ //
+ // The encoding uses about 4 bytes per vertex for typical polygons in
+ // Google's geographic repository, assuming that most vertices have been
+ // snapped to the centers of S2Cells at some fixed level (typically using
+ // InitToSnapped). The remaining vertices are stored using 24 bytes.
+ // Decoding a polygon encoded this way always returns the original polygon,
+ // without any loss of precision.
+ //
+ // The snap level is chosen to be the one that has the most vertices snapped
+ // to S2Cells at that level. If most vertices need 24 bytes, then all
+ // vertices are encoded this way (this method automatically chooses the
+ // encoding that has the best chance of giving the smaller output size).
+ //
+ // REQUIRES: "encoder" uses the default constructor, so that its buffer
+ // can be enlarged as necessary by calling Ensure(int).
+ void Encode(Encoder* const encoder) const;
+
+ // Encodes the polygon's S2Points directly as three doubles using
+ // (40 + 43 * num_loops + 24 * num_vertices) bytes.
+ //
+ // REQUIRES: "encoder" uses the default constructor, so that its buffer
+ // can be enlarged as necessary by calling Ensure(int).
+ void EncodeUncompressed(Encoder* encoder) const;
+
+ // Decodes a polygon encoded with Encode(). Returns true on success.
+ bool Decode(Decoder* const decoder);
+
+ // Decodes a polygon by pointing the S2Loop vertices directly into the
+ // decoder's memory buffer (which needs to persist for the lifetime of the
+ // decoded S2Polygon). It is much faster than Decode(), but requires that
+ // all the polygon vertices were encoded exactly using 24 bytes per vertex.
+ // This essentially requires that the polygon was not snapped beforehand to
+ // a given S2Cell level; otherwise this method falls back to Decode().
+ //
+ // Returns true on success.
+ bool DecodeWithinScope(Decoder* const decoder);
+
+#ifndef SWIG
+ // Wrapper class for indexing a polygon (see S2ShapeIndex). Once this
+ // object is inserted into an S2ShapeIndex it is owned by that index, and
+ // will be automatically deleted when no longer needed by the index. Note
+ // that this class does not take ownership of the polygon itself (see
+ // OwningShape below). You can also subtype this class to store additional
+ // data (see S2Shape for details).
+ //
+ // Note that unlike S2Polygon, the edges of S2Polygon::Shape are directed
+ // such that the polygon interior is always on the left.
+ class Shape : public S2Shape {
+ public:
+ static constexpr TypeTag kTypeTag = 1;
+
+ Shape() : polygon_(nullptr), cumulative_edges_(nullptr) {}
+ ~Shape() override;
+
+ // Initialization. Does not take ownership of "polygon". May be called
+ // more than once.
+ // TODO(ericv/jrosenstock): Make "polygon" a const reference.
+ explicit Shape(const S2Polygon* polygon);
+ void Init(const S2Polygon* polygon);
+
+ const S2Polygon* polygon() const { return polygon_; }
+
+ // Encodes the polygon using S2Polygon::Encode().
+ void Encode(Encoder* encoder) const {
+ polygon_->Encode(encoder);
+ }
+
+ // Encodes the polygon using S2Polygon::EncodeUncompressed().
+ void EncodeUncompressed(Encoder* encoder) const {
+ polygon_->EncodeUncompressed(encoder);
+ }
+
+ // Decoding is defined only for S2Polyline::OwningShape below.
+
+ // S2Shape interface:
+ int num_edges() const final { return num_edges_; }
+ Edge edge(int e) const final;
+ int dimension() const final { return 2; }
+ ReferencePoint GetReferencePoint() const final;
+ int num_chains() const final;
+ Chain chain(int i) const final;
+ Edge chain_edge(int i, int j) const final;
+ ChainPosition chain_position(int e) const final;
+ TypeTag type_tag() const override { return kTypeTag; }
+
+ private:
+ // The total number of edges in the polygon. This is the same as
+ // polygon_->num_vertices() except in one case (polygon_->is_full()). On
+ // the other hand this field doesn't take up any extra space due to field
+ // packing with S2Shape::id_.
+ //
+ // TODO(ericv): Consider using this field instead as an atomic<int> hint to
+ // speed up edge location when there are a large number of loops. Also
+ // consider changing S2Polygon::num_vertices to num_edges instead.
+ int num_edges_;
+
+ const S2Polygon* polygon_;
+
+ // An array where element "i" is the total number of edges in loops 0..i-1.
+ // This field is only used for polygons that have a large number of loops.
+ int* cumulative_edges_;
+ };
+
+ // Like Shape, except that the S2Polygon is automatically deleted when this
+ // object is deleted by the S2ShapeIndex. This is useful when an S2Polygon
+ // is constructed solely for the purpose of indexing it.
+ class OwningShape : public Shape {
+ public:
+ OwningShape() {} // Must call Init().
+
+ explicit OwningShape(std::unique_ptr<const S2Polygon> polygon)
+ : Shape(polygon.get()), owned_polygon_(std::move(polygon)) {}
+
+ void Init(std::unique_ptr<const S2Polygon> polygon) {
+ Shape::Init(polygon.get());
+ owned_polygon_ = std::move(polygon);
+ }
+
+ bool Init(Decoder* decoder) {
+ auto polygon = absl::make_unique<S2Polygon>();
+ if (!polygon->Decode(decoder)) return false;
+ Shape::Init(polygon.get());
+ owned_polygon_ = std::move(polygon);
+ return true;
+ }
+
+ std::unique_ptr<const S2Polygon> owned_polygon_;
+ };
+#endif // SWIG
+
+ // Returns the built-in S2ShapeIndex associated with every S2Polygon. This
+ // can be used in conjunction with the various S2ShapeIndex query classes
+ // (S2ClosestEdgeQuery, S2BooleanOperation, etc) to do things beyond what is
+ // possible with S2Polygon built-in convenience methods.
+ //
+ // For example, to measure the distance from one S2Polygon to another, you
+ // can write:
+ // S2ClosestEdgeQuery query(&polygon1.index());
+ // S2ClosestEdgeQuery::ShapeIndexTarget target(&polygon2.index());
+ // S1ChordAngle distance = query.GetDistance(&target);
+ //
+ // The index contains a single S2Polygon::Shape object.
+ const MutableS2ShapeIndex& index() const { return index_; }
+
+ private:
+ friend class S2Stats;
+ friend class PolygonOperation;
+
+ // Given that loops_ contains a single loop, initialize all other fields.
+ void InitOneLoop();
+
+ // Compute num_vertices_, bound_, subregion_bound_.
+ void InitLoopProperties();
+
+ // Deletes the contents of the loops_ vector and resets the polygon state.
+ void ClearLoops();
+
+ // Return true if there is an error in the loop nesting hierarchy.
+ bool FindLoopNestingError(S2Error* error) const;
+
+ // A map from each loop to its immediate children with respect to nesting.
+ // This map is built during initialization of multi-loop polygons to
+ // determine which are shells and which are holes, and then discarded.
+ typedef std::map<S2Loop*, std::vector<S2Loop*> > LoopMap;
+
+ void InsertLoop(S2Loop* new_loop, S2Loop* parent, LoopMap* loop_map);
+ void InitLoops(LoopMap* loop_map);
+
+ // Add the polygon's loops to the S2ShapeIndex. (The actual work of
+ // building the index only happens when the index is first used.)
+ void InitIndex();
+
+ // When the loop is modified (Invert(), or Init() called again) then the
+ // indexing structures need to be cleared since they become invalid.
+ void ClearIndex();
+
+ // Initializes the polygon to the result of the given boolean operation,
+ // returning an error on failure.
+ bool InitToOperation(S2BooleanOperation::OpType op_type,
+ const S2Builder::SnapFunction& snap_function,
+ const S2Polygon& a, const S2Polygon& b, S2Error* error);
+
+ // Initializes the polygon to the result of the given boolean operation,
+ // logging an error on failure (fatal in debug builds).
+ void InitToOperation(S2BooleanOperation::OpType op_type,
+ const S2Builder::SnapFunction& snap_function,
+ const S2Polygon& a, const S2Polygon& b);
+
+ // Initializes the polygon from input polygon "a" using the given S2Builder.
+ // If the result has an empty boundary (no loops), also decides whether the
+ // result should be the full polygon rather than the empty one based on the
+ // area of the input polygon. (See comments in InitToApproxIntersection.)
+ void InitFromBuilder(const S2Polygon& a, S2Builder* builder);
+
+ std::vector<std::unique_ptr<S2Polyline> > OperationWithPolyline(
+ S2BooleanOperation::OpType op_type,
+ const S2Builder::SnapFunction& snap_function,
+ const S2Polyline& a) const;
+
+ // Decode a polygon encoded with EncodeUncompressed(). Used by the Decode
+ // and DecodeWithinScope methods above. The within_scope parameter
+ // specifies whether to call DecodeWithinScope on the loops.
+ bool DecodeUncompressed(Decoder* const decoder, bool within_scope);
+
+ // Encode the polygon's vertices using about 4 bytes / vertex plus 24 bytes /
+ // unsnapped vertex. All the loop vertices must be converted first to the
+ // S2XYZFaceSiTi format using S2Loop::GetXYZFaceSiTiVertices, and concatenated
+ // in the all_vertices array.
+ //
+ // REQUIRES: snap_level >= 0.
+ void EncodeCompressed(Encoder* encoder, const S2XYZFaceSiTi* all_vertices,
+ int snap_level) const;
+
+ // Decode a polygon encoded with EncodeCompressed().
+ bool DecodeCompressed(Decoder* decoder);
+
+ static std::vector<std::unique_ptr<S2Polyline> > SimplifyEdgesInCell(
+ const S2Polygon& a, const S2Cell& cell,
+ double tolerance_uv, S1Angle snap_radius);
+
+ // Internal implementation of intersect/subtract polyline functions above.
+ std::vector<std::unique_ptr<S2Polyline> > InternalClipPolyline(
+ bool invert, const S2Polyline& a, S1Angle snap_radius) const;
+
+ // Defines a total ordering on S2Loops that does not depend on the cyclic
+ // order of loop vertices. This function is used to choose which loop to
+ // invert in the case where several loops have exactly the same area.
+ static int CompareLoops(const S2Loop* a, const S2Loop* b);
+
+ std::vector<std::unique_ptr<S2Loop> > loops_;
+
+ // Allows overriding the automatic validity checking controlled by the
+ // --s2debug flag.
+ S2Debug s2debug_override_;
+
+ // True if InitOriented() was called and the given loops had inconsistent
+ // orientations (i.e., it is not possible to construct a polygon such that
+ // the interior is on the left-hand side of all loops). We need to remember
+ // this error so that it can be returned later by FindValidationError(),
+ // since it is not possible to detect this error once the polygon has been
+ // initialized. This field is not preserved by Encode/Decode.
+ uint8 error_inconsistent_loop_orientations_;
+
+ // Cache for num_vertices().
+ int num_vertices_;
+
+ // In general we build the index the first time it is needed, but we make an
+ // exception for Contains(S2Point) because this method has a simple brute
+ // force implementation that is also relatively cheap. For this one method
+ // we keep track of the number of calls made and only build the index once
+ // enough calls have been made that we think an index would be worthwhile.
+ mutable std::atomic<int32> unindexed_contains_calls_;
+
+ // "bound_" is a conservative bound on all points contained by this polygon:
+ // if A.Contains(P), then A.bound_.Contains(S2LatLng(P)).
+ S2LatLngRect bound_;
+
+ // Since "bound_" is not exact, it is possible that a polygon A contains
+ // another polygon B whose bounds are slightly larger. "subregion_bound_"
+ // has been expanded sufficiently to account for this error, i.e.
+ // if A.Contains(B), then A.subregion_bound_.Contains(B.bound_).
+ S2LatLngRect subregion_bound_;
+
+ // Spatial index containing this polygon.
+ MutableS2ShapeIndex index_;
+
+#ifndef SWIG
+ S2Polygon(const S2Polygon&) = delete;
+ void operator=(const S2Polygon&) = delete;
+#endif
+};
+
+#endif // S2_S2POLYGON_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2POLYLINE_H_
+#define S2_S2POLYLINE_H_
+
+#include <memory>
+#include <vector>
+
+#include "s2/base/logging.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/s1angle.h"
+#include "s2/s2debug.h"
+#include "s2/s2error.h"
+#include "s2/s2latlng_rect.h"
+#include "s2/s2region.h"
+#include "s2/s2shape.h"
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/third_party/absl/memory/memory.h"
+
+class Decoder;
+class Encoder;
+class S1Angle;
+class S2Cap;
+class S2Cell;
+class S2LatLng;
+
+// An S2Polyline represents a sequence of zero or more vertices connected by
+// straight edges (geodesics). Edges of length 0 and 180 degrees are not
+// allowed, i.e. adjacent vertices should not be identical or antipodal.
+class S2Polyline final : public S2Region {
+ public:
+ // Creates an empty S2Polyline that should be initialized by calling Init()
+ // or Decode().
+ S2Polyline();
+
+#ifndef SWIG
+ // S2Polyline is movable, but only privately copyable.
+ S2Polyline(S2Polyline&&);
+ S2Polyline& operator=(S2Polyline&&);
+#endif // SWIG
+
+ // Convenience constructors that call Init() with the given vertices.
+ explicit S2Polyline(const std::vector<S2Point>& vertices);
+ explicit S2Polyline(const std::vector<S2LatLng>& vertices);
+
+ // Convenience constructors to disable the automatic validity checking
+ // controlled by the --s2debug flag. Example:
+ //
+ // S2Polyline* line = new S2Polyline(vertices, S2Debug::DISABLE);
+ //
+ // This is equivalent to:
+ //
+ // S2Polyline* line = new S2Polyline;
+ // line->set_s2debug_override(S2Debug::DISABLE);
+ // line->Init(vertices);
+ //
+ // The main reason to use this constructors is if you intend to call
+ // IsValid() explicitly. See set_s2debug_override() for details.
+ S2Polyline(const std::vector<S2Point>& vertices, S2Debug override);
+ S2Polyline(const std::vector<S2LatLng>& vertices, S2Debug override);
+
+ // Initialize a polyline that connects the given vertices. Empty polylines are
+ // allowed. Adjacent vertices should not be identical or antipodal. All
+ // vertices should be unit length.
+ void Init(const std::vector<S2Point>& vertices);
+
+ // Convenience initialization function that accepts latitude-longitude
+ // coordinates rather than S2Points.
+ void Init(const std::vector<S2LatLng>& vertices);
+
+ ~S2Polyline() override;
+
+ // Allows overriding the automatic validity checks controlled by the
+ // --s2debug flag. If this flag is true, then polylines are automatically
+ // checked for validity as they are initialized. The main reason to disable
+ // this flag is if you intend to call IsValid() explicitly, like this:
+ //
+ // S2Polyline line;
+ // line.set_s2debug_override(S2Debug::DISABLE);
+ // line.Init(...);
+ // if (!line.IsValid()) { ... }
+ //
+ // Without the call to set_s2debug_override(), invalid data would cause a
+ // fatal error in Init() whenever the --s2debug flag is enabled.
+ void set_s2debug_override(S2Debug override);
+ S2Debug s2debug_override() const;
+
+ // Return true if the given vertices form a valid polyline.
+ bool IsValid() const;
+
+ // Returns true if this is *not* a valid polyline and sets "error"
+ // appropriately. Otherwise returns false and leaves "error" unchanged.
+ //
+ // REQUIRES: error != nullptr
+ bool FindValidationError(S2Error* error) const;
+
+ int num_vertices() const { return num_vertices_; }
+ const S2Point& vertex(int k) const {
+ S2_DCHECK_GE(k, 0);
+ S2_DCHECK_LT(k, num_vertices_);
+ return vertices_[k];
+ }
+
+ // Return the length of the polyline.
+ S1Angle GetLength() const;
+
+ // Return the true centroid of the polyline multiplied by the length of the
+ // polyline (see s2centroids.h for details on centroids). The result is not
+ // unit length, so you may want to normalize it.
+ //
+ // Prescaling by the polyline length makes it easy to compute the centroid
+ // of several polylines (by simply adding up their centroids).
+ S2Point GetCentroid() const;
+
+ // Return the point whose distance from vertex 0 along the polyline is the
+ // given fraction of the polyline's total length. Fractions less than zero
+ // or greater than one are clamped. The return value is unit length. This
+ // cost of this function is currently linear in the number of vertices.
+ // The polyline must not be empty.
+ S2Point Interpolate(double fraction) const;
+
+ // Like Interpolate(), but also return the index of the next polyline
+ // vertex after the interpolated point P. This allows the caller to easily
+ // construct a given suffix of the polyline by concatenating P with the
+ // polyline vertices starting at "next_vertex". Note that P is guaranteed
+ // to be different than vertex(*next_vertex), so this will never result in
+ // a duplicate vertex.
+ //
+ // The polyline must not be empty. Note that if "fraction" >= 1.0, then
+ // "next_vertex" will be set to num_vertices() (indicating that no vertices
+ // from the polyline need to be appended). The value of "next_vertex" is
+ // always between 1 and num_vertices().
+ //
+ // This method can also be used to construct a prefix of the polyline, by
+ // taking the polyline vertices up to "next_vertex - 1" and appending the
+ // returned point P if it is different from the last vertex (since in this
+ // case there is no guarantee of distinctness).
+ S2Point GetSuffix(double fraction, int* next_vertex) const;
+
+ // The inverse operation of GetSuffix/Interpolate. Given a point on the
+ // polyline, returns the ratio of the distance to the point from the
+ // beginning of the polyline over the length of the polyline. The return
+ // value is always betwen 0 and 1 inclusive. See GetSuffix() for the
+ // meaning of "next_vertex".
+ //
+ // The polyline should not be empty. If it has fewer than 2 vertices, the
+ // return value is zero.
+ double UnInterpolate(const S2Point& point, int next_vertex) const;
+
+ // Given a point, returns a point on the polyline that is closest to the given
+ // point. See GetSuffix() for the meaning of "next_vertex", which is chosen
+ // here w.r.t. the projected point as opposed to the interpolated point in
+ // GetSuffix().
+ //
+ // The polyline must be non-empty.
+ S2Point Project(const S2Point& point, int* next_vertex) const;
+
+ // Returns true if the point given is on the right hand side of the polyline,
+ // using a naive definition of "right-hand-sideness" where the point is on
+ // the RHS of the polyline iff the point is on the RHS of the line segment in
+ // the polyline which it is closest to.
+ //
+ // The polyline must have at least 2 vertices.
+ bool IsOnRight(const S2Point& point) const;
+
+ // Return true if this polyline intersects the given polyline. If the
+ // polylines share a vertex they are considered to be intersecting. When a
+ // polyline endpoint is the only intersection with the other polyline, the
+ // function may return true or false arbitrarily.
+ //
+ // The running time is quadratic in the number of vertices. (To intersect
+ // polylines more efficiently, or compute the actual intersection geometry,
+ // use S2BooleanOperation.)
+ bool Intersects(const S2Polyline* line) const;
+
+ // Reverse the order of the polyline vertices.
+ void Reverse();
+
+ // Return a subsequence of vertex indices such that the polyline connecting
+ // these vertices is never further than "tolerance" from the original
+ // polyline. Provided the first and last vertices are distinct, they are
+ // always preserved; if they are not, the subsequence may contain only a
+ // single index.
+ //
+ // Some useful properties of the algorithm:
+ //
+ // - It runs in linear time.
+ //
+ // - The output is always a valid polyline. In particular, adjacent
+ // output vertices are never identical or antipodal.
+ //
+ // - The method is not optimal, but it tends to produce 2-3% fewer
+ // vertices than the Douglas-Peucker algorithm with the same tolerance.
+ //
+ // - The output is *parametrically* equivalent to the original polyline to
+ // within the given tolerance. For example, if a polyline backtracks on
+ // itself and then proceeds onwards, the backtracking will be preserved
+ // (to within the given tolerance). This is different than the
+ // Douglas-Peucker algorithm, which only guarantees geometric equivalence.
+ //
+ // See also S2PolylineSimplifier, which uses the same algorithm but is more
+ // efficient and supports more features, and also S2Builder, which can
+ // simplify polylines and polygons, supports snapping (e.g. to E7 lat/lng
+ // coordinates or S2CellId centers), and can split polylines at intersection
+ // points.
+ void SubsampleVertices(S1Angle tolerance, std::vector<int>* indices) const;
+
+ // Return true if two polylines are exactly the same.
+ bool Equals(const S2Polyline* b) const;
+
+ // Return true if two polylines have the same number of vertices, and
+ // corresponding vertex pairs are separated by no more than "max_error".
+ // (For testing purposes.)
+ bool ApproxEquals(const S2Polyline& b,
+ S1Angle max_error = S1Angle::Radians(1e-15)) const;
+
+ // Return true if "covered" is within "max_error" of a contiguous subpath of
+ // this polyline over its entire length. Specifically, this method returns
+ // true if this polyline has parameterization a:[0,1] -> S^2, "covered" has
+ // parameterization b:[0,1] -> S^2, and there is a non-decreasing function
+ // f:[0,1] -> [0,1] such that distance(a(f(t)), b(t)) <= max_error for all t.
+ //
+ // You can think of this as testing whether it is possible to drive a car
+ // along "covered" and a car along some subpath of this polyline such that no
+ // car ever goes backward, and the cars are always within "max_error" of each
+ // other.
+ //
+ // This function is well-defined for empty polylines:
+ // anything.covers(empty) = true
+ // empty.covers(nonempty) = false
+ bool NearlyCovers(const S2Polyline& covered, S1Angle max_error) const;
+
+ // Returns the total number of bytes used by the polyline.
+ size_t SpaceUsed() const;
+
+ ////////////////////////////////////////////////////////////////////////
+ // S2Region interface (see s2region.h for details):
+
+ S2Polyline* Clone() const override;
+ S2Cap GetCapBound() const override;
+ S2LatLngRect GetRectBound() const override;
+ bool Contains(const S2Cell& cell) const override { return false; }
+ bool MayIntersect(const S2Cell& cell) const override;
+
+ // Always return false, because "containment" is not numerically
+ // well-defined except at the polyline vertices.
+ bool Contains(const S2Point& p) const override { return false; }
+
+ // Appends a serialized representation of the S2Polyline to "encoder".
+ //
+ // REQUIRES: "encoder" uses the default constructor, so that its buffer
+ // can be enlarged as necessary by calling Ensure(int).
+ void Encode(Encoder* const encoder) const;
+
+ // Decodes an S2Polyline encoded with Encode(). Returns true on success.
+ bool Decode(Decoder* const decoder);
+
+#ifndef SWIG
+ // Wrapper class for indexing a polyline (see S2ShapeIndex). Once this
+ // object is inserted into an S2ShapeIndex it is owned by that index, and
+ // will be automatically deleted when no longer needed by the index. Note
+ // that this class does not take ownership of the polyline itself (see
+ // OwningShape below). You can also subtype this class to store additional
+ // data (see S2Shape for details).
+ class Shape : public S2Shape {
+ public:
+ static constexpr TypeTag kTypeTag = 2;
+
+ Shape() : polyline_(nullptr) {} // Must call Init().
+
+ // Initialization. Does not take ownership of "polyline".
+ //
+ // Note that a polyline with one vertex is defined to have no edges. Use
+ // S2LaxPolylineShape or S2LaxClosedPolylineShape if you want to define a
+ // polyline consisting of a single degenerate edge.
+ explicit Shape(const S2Polyline* polyline) { Init(polyline); }
+ void Init(const S2Polyline* polyline);
+
+ const S2Polyline* polyline() const { return polyline_; }
+
+ // Encodes the polyline using S2Polyline::Encode().
+ void Encode(Encoder* encoder) const {
+ // TODO(geometry-library): Support compressed encodings.
+ polyline_->Encode(encoder);
+ }
+
+ // Decoding is defined only for S2Polyline::OwningShape below.
+
+ // S2Shape interface:
+
+ int num_edges() const final {
+ return std::max(0, polyline_->num_vertices() - 1);
+ }
+ Edge edge(int e) const final {
+ return Edge(polyline_->vertex(e), polyline_->vertex(e + 1));
+ }
+ int dimension() const final { return 1; }
+ ReferencePoint GetReferencePoint() const final {
+ return ReferencePoint::Contained(false);
+ }
+ int num_chains() const final;
+ Chain chain(int i) const final;
+ Edge chain_edge(int i, int j) const final {
+ S2_DCHECK_EQ(i, 0);
+ return Edge(polyline_->vertex(j), polyline_->vertex(j + 1));
+ }
+ ChainPosition chain_position(int e) const final {
+ return ChainPosition(0, e);
+ }
+ TypeTag type_tag() const override { return kTypeTag; }
+
+ private:
+ const S2Polyline* polyline_;
+ };
+
+ // Like Shape, except that the S2Polyline is automatically deleted when this
+ // object is deleted by the S2ShapeIndex. This is useful when an S2Polyline
+ // is constructed solely for the purpose of indexing it.
+ class OwningShape : public Shape {
+ public:
+ OwningShape() {} // Must call Init().
+
+ explicit OwningShape(std::unique_ptr<const S2Polyline> polyline)
+ : Shape(polyline.get()), owned_polyline_(std::move(polyline)) {}
+
+ void Init(std::unique_ptr<const S2Polyline> polyline) {
+ Shape::Init(polyline.get());
+ owned_polyline_ = std::move(polyline);
+ }
+
+ bool Init(Decoder* decoder) {
+ auto polyline = absl::make_unique<S2Polyline>();
+ if (!polyline->Decode(decoder)) return false;
+ Shape::Init(polyline.get());
+ owned_polyline_ = std::move(polyline);
+ return true;
+ }
+
+ private:
+ std::unique_ptr<const S2Polyline> owned_polyline_;
+ };
+#endif // SWIG
+
+ private:
+ // Internal copy constructor used only by Clone() that makes a deep copy of
+ // its argument.
+ S2Polyline(const S2Polyline& src);
+
+ // Allows overriding the automatic validity checking controlled by the
+ // --s2debug flag.
+ S2Debug s2debug_override_;
+
+ // We store the vertices in an array rather than a vector because we don't
+ // need any STL methods, and computing the number of vertices using size()
+ // would be relatively expensive (due to division by sizeof(S2Point) == 24).
+ int num_vertices_ = 0;
+ std::unique_ptr<S2Point[]> vertices_;
+
+#ifndef SWIG
+ void operator=(const S2Polyline&) = delete;
+#endif // SWIG
+};
+
+#endif // S2_S2POLYLINE_H_
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+
+#ifndef S2_S2POLYLINE_ALIGNMENT_H_
+#define S2_S2POLYLINE_ALIGNMENT_H_
+
+#include <memory>
+#include <vector>
+
+#include "s2/s2polyline.h"
+
+// This library provides code to compute vertex alignments between S2Polylines.
+//
+// A vertex "alignment" or "warp" between two polylines is a matching between
+// pairs of their vertices. Users can imagine pairing each vertex from
+// S2Polyline `a` with at least one other vertex in S2Polyline `b`. The "cost"
+// of an arbitrary alignment is defined as the summed value of the squared
+// chordal distance between each pair of points in the warp path. An "optimal
+// alignment" for a pair of polylines is defined as the alignment with least
+// cost. Note: optimal alignments are not necessarily unique. The standard way
+// of computing an optimal alignment between two sequences is the use of the
+// `Dynamic Timewarp` algorithm.
+//
+// We provide three methods for computing (via Dynamic Timewarp) the optimal
+// alignment between two S2Polylines. These methods are performance-sensitive,
+// and have been reasonably optimized for space- and time- usage. On modern
+// hardware, it is possible to compute exact alignments between 4096x4096
+// element polylines in ~70ms, and approximate alignments much more quickly.
+//
+// The results of an alignment operation are captured in a VertexAlignment
+// object. In particular, a VertexAlignment keeps track of the total cost of
+// alignment, as well as the warp path (a sequence of pairs of indices into each
+// polyline whose vertices are linked together in the optimal alignment)
+//
+// For a worked example, consider the polylines
+//
+// a = [(1, 0), (5, 0), (6, 0), (9, 0)] and
+// b = [(2, 0), (7, 0), (8, 0)].
+//
+// The "cost matrix" between these two polylines (using squared chordal
+// distance, .Norm2(), as our distance function) looks like this:
+//
+// (2, 0) (7, 0) (8, 0)
+// (1, 0) 1 36 49
+// (5, 0) 9 4 9
+// (6, 0) 16 1 4
+// (9, 0) 49 4 1
+//
+// The Dynamic Timewarp DP table for this cost matrix has cells defined by
+//
+// table[i][j] = cost(i,j) + min(table[i-1][j-1], table[i][j-1], table[i-1, j])
+//
+// (2, 0) (7, 0) (8, 0)
+// (1, 0) 1 37 86
+// (5, 0) 10 5 14
+// (6, 0) 26 6 9
+// (9, 0) 75 10 7
+//
+// Starting at the bottom right corner of the DP table, we can work our way
+// backwards to the upper left corner to recover the reverse of the warp path:
+// (3, 2) -> (2, 1) -> (1, 1) -> (0, 0). The VertexAlignment produced containing
+// this has alignment_cost = 7 and warp_path = {(0, 0), (1, 1), (2, 1), (3, 2)}.
+//
+// We also provide methods for performing alignment of multiple sequences. These
+// methods return a single, representative polyline from a non-empty collection
+// of polylines, for various definitions of "representative."
+//
+// GetMedoidPolyline() returns a new polyline (point-for-point-equal to some
+// existing polyline from the collection) that minimizes the summed vertex
+// alignment cost to all other polylines in the collection.
+//
+// GetConsensusPolyline() returns a new polyline (unlikely to be present in the
+// input collection) that represents a "weighted consensus" polyline. This
+// polyline is constructed iteratively using the Dynamic Timewarp Barycenter
+// Averaging algorithm of F. Petitjean, A. Ketterlin, and P. Gancarski, which
+// can be found here:
+// https://pdfs.semanticscholar.org/a596/8ca9488199291ffe5473643142862293d69d.pdf
+
+namespace s2polyline_alignment {
+
+typedef std::vector<std::pair<int, int>> WarpPath;
+
+struct VertexAlignment {
+ // `alignment_cost` represents the sum of the squared chordal distances
+ // between each pair of vertices in the warp path. Specifically,
+ // cost = sum_{(i, j) \in path} (a.vertex(i) - b.vertex(j)).Norm2();
+ // This means that the units of alignment_cost are "squared distance". This is
+ // an optimization to avoid the (expensive) atan computation of the true
+ // spherical angular distance between the points, as well as an unnecessary
+ // square root. All we need to compute vertex alignment is a metric that
+ // satisifies the triangle inequality, and squared chordal distance works as
+ // well as spherical S1Angle distance for this purpose.
+ double alignment_cost;
+
+ // Each entry (i, j) of `warp_path` represents a pairing between vertex
+ // a.vertex(i) and vertex b.vertex(j) in the optimal alignment.
+ // The warp_path is defined in forward order, such that the result of
+ // aligning polylines `a` and `b` is always a warp_path with warp_path.front()
+ // = {0,0} and warp_path.back() = {a.num_vertices() - 1, b.num_vertices() - 1}
+ // Note that this DOES NOT define an alignment from a point sequence to an
+ // edge sequence. That functionality may come at a later date.
+ WarpPath warp_path;
+
+ VertexAlignment(const double cost, const WarpPath& path)
+ : alignment_cost(cost), warp_path(path) {}
+};
+
+// GetExactVertexAlignment takes two non-empty polylines as input, and returns
+// the VertexAlignment corresponding to the optimal alignment between them. This
+// method is quadratic O(A*B) in both space and time complexity.
+VertexAlignment GetExactVertexAlignment(const S2Polyline& a,
+ const S2Polyline& b);
+
+// GetExactVertexAlignmentCost takes two non-empty polylines as input, and
+// returns the *cost* of their optimal alignment. A standard, traditional
+// dynamic timewarp algorithm can output both a warp path and a cost, but
+// requires quadratic space to reconstruct the path by walking back through the
+// Dynamic Programming cost table. If all you really need is the warp cost (i.e.
+// you're inducing a similarity metric between S2Polylines, or something
+// equivalent), you can overwrite the DP table and use constant space -
+// O(max(A,B)). This method provides that space-efficiency optimization.
+double GetExactVertexAlignmentCost(const S2Polyline& a, const S2Polyline& b);
+
+// GetApproxVertexAlignment takes two non-empty polylines `a` and `b` as input,
+// and a `radius` paramater GetApproxVertexAlignment (quickly) computes an
+// approximately optimal vertex alignment of points between polylines `a` and
+// `b` by implementing the algorithm described in `FastDTW: Toward Accurate
+// Dynamic Time Warping in Linear Time and Space` by Stan Salvador and Philip
+// Chan. Details can be found below:
+//
+// https://pdfs.semanticscholar.org/05a2/0cde15e172fc82f32774dd0cf4fe5827cad2.pdf
+//
+// The `radius` parameter controls the distance we search outside of the
+// projected warp path during the refining step. Smaller values of `radius`
+// correspond to a smaller search window, and therefore distance computation on
+// fewer cells, which leads to a faster (but worse) approximation.
+// This method is O(max(A, B)) in both space and time complexity.
+VertexAlignment GetApproxVertexAlignment(const S2Polyline& a,
+ const S2Polyline& b, const int radius);
+
+// A convience overload for GetApproxVertexAlignment which computes and uses
+// suggested default parameter of radius = max(a.size(), b.size())^0.25
+VertexAlignment GetApproxVertexAlignment(const S2Polyline& a,
+ const S2Polyline& b);
+
+// GetMedoidPolyline returns the index `p` of a "medoid" polyline from a
+// non-empty collection of `polylines` such that
+//
+// sum_{all j in `polylines`} VertexAlignmentCost(p, j) is minimized.
+//
+// In the case of a tie for minimal summed alignment cost, we return the lowest
+// index - this tie is guaranteed to happen in the two-polyline-input case.
+//
+// ASYMPTOTIC BEHAVIOR:
+// Computation may require up to (N^2 - N) / 2 alignment cost function
+// evaluations, for N input polylines. For polylines of length U, V, the
+// alignment cost function evaluation is O(U+V) if options.approx = true and
+// O(U*V) if options.approx = false.
+
+class MedoidOptions {
+ public:
+ // If options.approx = false, we compute vertex alignment costs exactly.
+ // If options.approx = true, we use approximate vertex alignment
+ // computation, called with the default radius parameter.
+ bool approx() const { return approx_; }
+ void set_approx(bool approx) { approx_ = approx; }
+
+ private:
+ bool approx_ = true;
+};
+
+int GetMedoidPolyline(const std::vector<std::unique_ptr<S2Polyline>>& polylines,
+ const MedoidOptions options);
+
+// GetConsensusPolyline allocates and returns a new "consensus" polyline from a
+// non-empty collection of polylines. We iteratively apply Dynamic Timewarp
+// Barycenter Averaging to an initial `seed` polyline, which improves the
+// consensus alignment quality each iteration. For implementation details, see
+//
+// https://pdfs.semanticscholar.org/a596/8ca9488199291ffe5473643142862293d69d.pdf
+//
+// The returned polyline from this method is unlikely to be point-for-point
+// equal to an input polyline, whereas a polyline returned from
+// GetMedoidPolyline() is guaranteed to match an input polyline point-for-point.
+// NOTE: the number of points in our returned consensus polyline is always equal
+// to the number of points in the initial seed, which is implementation-defined.
+// If the collection of polylines has a large resolution distribution, it might
+// be a good idea to reinterpolate them to have about the same number of points.
+// In practice, this doesn't seem to matter, but is probably worth noting.
+//
+// ASYMPTOTIC BEHAVIOR:
+// Seeding this algorithm requires O(1) vertex alignments if seed_medoid =
+// false, and O(N^2) vertex alignments if seed_medoid = true. Once the seed
+// polyline is chosen, computing the consensus polyline requires at most
+// (iteration_cap)*N vertex alignments. For polylines of length U, V, the
+// alignment cost function evaluation is O(U+V) if options.approx = true, and
+// O(U*V) if options.approx = false.
+
+class ConsensusOptions {
+ public:
+ // If options.approx = false, vertex alignments are computed with
+ // GetExactVertexAlignment. If options.approx = true, vertex alignments are
+ // computed with GetApproxVertexAlignment, called with default radius
+ // parameter.
+ bool approx() const { return approx_; }
+ void set_approx(bool approx) { approx_ = approx; }
+
+ // If options.seed_medoid = true, we seed the consensus polyline with the
+ // medoid of the collection. This is a more expensive approach, but may result
+ // in higher quality consensus sequences by avoiding bad arbitrary initial
+ // seeds. Seeding with the medoid will incur up to (N^2 - N) / 2 evaluations
+ // of the vertex alignment function. If options.seed_medoid = false, we seed
+ // the consensus polyline by taking an arbitrary element from the collection.
+ bool seed_medoid() const { return seed_medoid_; }
+ void set_seed_medoid(bool seed_medoid) { seed_medoid_ = seed_medoid; }
+
+ // options.iteration_cap controls the maximum number of DBA refining steps we
+ // apply to the initial seed.
+ int iteration_cap() const { return iteration_cap_; }
+ void set_iteration_cap(int iteration_cap) { iteration_cap_ = iteration_cap; }
+
+ private:
+ bool approx_ = true;
+ bool seed_medoid_ = false;
+ int iteration_cap_ = 5;
+};
+
+std::unique_ptr<S2Polyline> GetConsensusPolyline(
+ const std::vector<std::unique_ptr<S2Polyline>>& polylines,
+ const ConsensusOptions options);
+} // namespace s2polyline_alignment
+#endif // S2_S2POLYLINE_ALIGNMENT_H_
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+
+#ifndef S2_S2POLYLINE_ALIGNMENT_INTERNAL_H_
+#define S2_S2POLYLINE_ALIGNMENT_INTERNAL_H_
+
+#include "s2/s2polyline_alignment.h"
+
+#include <limits>
+#include <vector>
+
+#include "s2/s2polyline.h"
+
+namespace s2polyline_alignment {
+
+static constexpr double DOUBLE_MAX = std::numeric_limits<double>::max();
+
+// Alias for a 2d Dynamic Programming table.
+typedef std::vector<std::vector<double>> CostTable;
+
+// A ColumnStride is a [start, end) range of columns in a search window.
+// It enables us to lazily fill up our CostTable structures by providing bounds
+// checked access for reads. We also use them to keep track of structured,
+// sparse Window matrices by tracking start and end columns for each row.
+struct ColumnStride {
+ int start;
+ int end;
+ inline bool InRange(const int index) const {
+ return start <= index && index < end;
+ }
+ // Returns a ColumnStride where InRange evaluates to `true` for all
+ // non-negative inputs less than INT_MAX;
+ static inline constexpr ColumnStride All() {
+ return ColumnStride{-1, std::numeric_limits<int>::max()};
+ }
+};
+
+// A Window is a sparse binary matrix with specific structural constraints
+// on allowed element configurations. It is used in this library to represent
+// "search windows" for windowed dynamic timewarping.
+//
+// Valid Windows require the following structural conditions to hold:
+// 1) All rows must consist of a single contiguous stride of `true` values.
+// 2) All strides are greater than zero length (i.e. no empty rows).
+// 3) The index of the first `true` column in a row must be at least as
+// large as the index of the first `true` column in the previous row.
+// 4) The index of the last `true` column in a row must be at least as large
+// as the index of the last `true` column in the previous row.
+// 5) strides[0].start = 0 (the first cell is always filled).
+// 6) strides[n_rows-1].end = n_cols (the last cell is filled).
+//
+// Example valid strided_masks (* = filled, . = unfilled)
+// 0 1 2 3 4 5
+// 0 * * * . . .
+// 1 . * * * . .
+// 2 . * * * . .
+// 3 . . * * * *
+// 4 . . * * * *
+// 0 1 2 3 4 5
+// 0 * * * * . .
+// 1 . * * * * .
+// 2 . . * * * .
+// 3 . . . . * *
+// 4 . . . . . *
+// 0 1 2 3 4 5
+// 0 * * . . . .
+// 1 . * . . . .
+// 2 . . * * * .
+// 3 . . . . . *
+// 4 . . . . . *
+//
+// Example invalid strided_masks:
+// 0 1 2 3 4 5
+// 0 * * * . * * <-- more than one continuous run
+// 1 . * * * . .
+// 2 . * * * . .
+// 3 . . * * * *
+// 4 . . * * * *
+// 0 1 2 3 4 5
+// 0 * * * . . .
+// 1 . * * * . .
+// 2 . * * * . .
+// 3 * * * * * * <-- start index not monotonically increasing
+// 4 . . * * * *
+// 0 1 2 3 4 5
+// 0 * * * . . .
+// 1 . * * * * .
+// 2 . * * * . . <-- end index not monotonically increasing
+// 3 . . * * * *
+// 4 . . * * * *
+// 0 1 2 3 4 5
+// 0 . * . . . . <-- does not fill upper left corner
+// 1 . * . . . .
+// 2 . * . . . .
+// 3 . * * * . .
+// 4 . . * * * *
+class Window {
+ public:
+ // Construct a Window from a non-empty list of column strides.
+ explicit Window(const std::vector<ColumnStride>& strides);
+
+ // Construct a Window from a non-empty sequence of warp path index pairs.
+ explicit Window(const WarpPath& warp_path);
+
+ // Return the (not-bounds-checked) stride for this row.
+ inline ColumnStride GetColumnStride(const int row) const {
+ return strides_[row];
+ }
+
+ // Return the (bounds-checked) stride for this row.
+ // If row < 0, returns ColumnStride::All()
+ inline ColumnStride GetCheckedColumnStride(const int row) const {
+ return (row > 0) ? strides_[row] : ColumnStride::All();
+ }
+
+ // Return a new, larger Window that is an upscaled version of this window
+ // Used by ApproximateAlignment window expansion step.
+ Window Upsample(const int new_rows, const int new_cols) const;
+
+ // Return a new, equal-size Window by dilating this window with a square
+ // structuring element with half-length `radius`. Radius = 1 corresponds to
+ // a 3x3 square morphological dilation.
+ // Used by ApproximateAlignment window expansion step.
+ Window Dilate(const int radius) const;
+
+ // Return a string representation of this window.
+ string DebugString() const;
+
+ private:
+ int rows_;
+ int cols_;
+ std::vector<ColumnStride> strides_;
+ // Returns true if this window's data represents a valid window.
+ bool IsValid() const;
+};
+
+// Reduce the number of vertices of polyline `in` by selecting every other
+// vertex for inclusion in a new polyline. Specifically, we take even-index
+// vertices [0, 2, 4,...]. For an even-length polyline, the last vertex is not
+// selected. For an odd-length polyline, the last vertex is selected.
+// Constructs and returns a new S2Polyline in linear time.
+std::unique_ptr<S2Polyline> HalfResolution(const S2Polyline& in);
+
+} // namespace s2polyline_alignment
+#endif // S2_S2POLYLINE_ALIGNMENT_INTERNAL_H_
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// Defines various measures for polylines on the sphere. These are low-level
+// methods that work directly with arrays of S2Points. They are used to
+// implement the methods in s2shapeindex_measures.h, s2shape_measures.h, and
+// s2polyline.h.
+//
+// See s2loop_measures.h, s2edge_distances.h, and s2measures.h for additional
+// low-level methods.
+
+#ifndef S2_S2POLYLINE_MEASURES_H_
+#define S2_S2POLYLINE_MEASURES_H_
+
+#include "s2/s1angle.h"
+#include "s2/s2point.h"
+#include "s2/s2point_span.h"
+
+namespace S2 {
+
+// Returns the length of the polyline. Returns zero for polylines with fewer
+// than two vertices.
+S1Angle GetLength(S2PointSpan polyline);
+
+// Returns the true centroid of the polyline multiplied by the length of the
+// polyline (see s2centroids.h for details on centroids). The result is not
+// unit length, so you may want to normalize it.
+//
+// Scaling by the polyline length makes it easy to compute the centroid of
+// several polylines (by simply adding up their centroids).
+//
+// CAVEAT: Returns S2Point() for degenerate polylines (e.g., AA). [Note that
+// this answer is correct; the result of this function is a line integral over
+// the polyline, whose value is always zero if the polyline is degenerate.]
+S2Point GetCentroid(S2PointSpan polyline);
+
+} // namespace S2
+
+#endif // S2_S2POLYLINE_MEASURES_H_
--- /dev/null
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// This is a helper class for simplifying polylines. It allows you to compute
+// a maximal edge that intersects a sequence of discs, and that optionally
+// avoids a different sequence of discs. The results are conservative in that
+// the edge is guaranteed to intersect or avoid the specified discs using
+// exact arithmetic (see s2predicates.h).
+//
+// Note that S2Builder can also simplify polylines and supports more features
+// (e.g., snapping to S2CellId centers), so it is only recommended to use this
+// class if S2Builder does not meet your needs.
+//
+// Here is a simple example showing how to simplify a polyline into a sequence
+// of edges that stay within "max_error" of the original edges:
+//
+// vector<S2Point> v = { ... };
+// S2PolylineSimplifier simplifier;
+// simplifier.Init(v[0]);
+// for (int i = 1; i < v.size(); ++i) {
+// if (!simplifier.Extend(v[i])) {
+// OutputEdge(simplifier.src(), v[i-1]);
+// simplifier.Init(v[i-1]);
+// }
+// simplifier.TargetDisc(v[i], max_error);
+// }
+// OutputEdge(simplifer.src(), v.back());
+//
+// Note that the points targeted by TargetDisc do not need to be the same as
+// the candidate endpoints passed to Extend. So for example, you could target
+// the original vertices of a polyline, but only consider endpoints that are
+// snapped to E7 coordinates or S2CellId centers.
+//
+// Please be aware that this class works by maintaining a range of acceptable
+// angles (bearings) from the start vertex to the hypothetical destination
+// vertex. It does not keep track of distances to any of the discs to be
+// targeted or avoided. Therefore to use this class correctly, constraints
+// should be added in increasing order of distance. (The actual requirement
+// is slightly weaker than this, which is why it is not enforced, but
+// basically you should only call TargetDisc() and AvoidDisc() with arguments
+// that you want to constrain the immediately following call to Extend().)
+
+#ifndef S2_S2POLYLINE_SIMPLIFIER_H_
+#define S2_S2POLYLINE_SIMPLIFIER_H_
+
+#include "s2/_fp_contract_off.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s1interval.h"
+
+class S2PolylineSimplifier {
+ public:
+ S2PolylineSimplifier() {}
+
+ // Starts a new simplified edge at "src".
+ void Init(const S2Point& src);
+
+ // Returns the source vertex of the output edge.
+ S2Point src() const;
+
+ // Returns true if the edge (src, dst) satisfies all of the targeting
+ // requirements so far. Returns false if the edge would be longer than
+ // 90 degrees (such edges are not supported).
+ bool Extend(const S2Point& dst) const;
+
+ // Requires that the output edge must pass through the given disc.
+ bool TargetDisc(const S2Point& point, S1ChordAngle radius);
+
+ // Requires that the output edge must avoid the given disc. "disc_on_left"
+ // specifies whether the disc must be to the left or right of the edge.
+ // (This feature allows the simplified edge to preserve the topology of the
+ // original polyline with respect to other nearby points.)
+ //
+ // If your input is a polyline, you can compute "disc_on_left" as follows.
+ // Let the polyline be ABCDE and assume that it already avoids a set of
+ // points X_i. Suppose that you have aleady added ABC to the simplifer, and
+ // now want to extend the edge chain to D. First find the X_i that are near
+ // the edge CD, then discard the ones such that AX_i <= AC or AX_i >= AD
+ // (since these points have either already been considered or aren't
+ // relevant yet). Now X_i is to the left of the polyline if and only if
+ // s2pred::OrderedCCW(A, D, X, C) (in other words, if X_i is to the left of
+ // the angle wedge ACD).
+ bool AvoidDisc(const S2Point& point, S1ChordAngle radius,
+ bool disc_on_left);
+
+ private:
+ double GetAngle(const S2Point& p) const;
+ double GetSemiwidth(const S2Point& p, S1ChordAngle r,
+ int round_direction) const;
+
+ S2Point src_;
+ S2Point x_dir_, y_dir_;
+ S1Interval window_;
+};
+
+#endif // S2_S2POLYLINE_SIMPLIFIER_H_
--- /dev/null
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// This class contains various predicates that are guaranteed to produce
+// correct, consistent results. They are also relatively efficient. This is
+// achieved by computing conservative error bounds and falling back to high
+// precision or even exact arithmetic when the result is uncertain. Such
+// predicates are useful in implementing robust algorithms.
+//
+// See also S2EdgeCrosser, which implements various exact
+// edge-crossing predicates more efficiently than can be done here.
+//
+// TODO(ericv): Add InCircleSign() (the Voronoi/Delaunay predicate).
+// (This is trickier than the usual textbook implementations because we want
+// to model S2Points as lying exactly on the mathematical unit sphere.)
+
+#ifndef S2_S2PREDICATES_H_
+#define S2_S2PREDICATES_H_
+
+#include <cfloat>
+#include <iosfwd>
+
+#include "s2/_fp_contract_off.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s2debug.h"
+#include "s2/s2pointutil.h"
+
+namespace s2pred {
+
+// S2EdgeUtil contains the following exact predicates that test for edge
+// crossings. (Usually you will want to use S2EdgeCrosser, which
+// implements them much more efficiently.)
+//
+// int CrossingSign(const S2Point& a0, const S2Point& a1,
+// const S2Point& b0, const S2Point& b1);
+//
+// bool EdgeOrVertexCrossing(const S2Point& a0, const S2Point& a1,
+// const S2Point& b0, const S2Point& b1);
+
+// Returns +1 if the points A, B, C are counterclockwise, -1 if the points
+// are clockwise, and 0 if any two points are the same. This function is
+// essentially like taking the sign of the determinant of ABC, except that
+// it has additional logic to make sure that the above properties hold even
+// when the three points are coplanar, and to deal with the limitations of
+// floating-point arithmetic.
+//
+// Sign satisfies the following conditions:
+//
+// (1) Sign(a,b,c) == 0 if and only if a == b, b == c, or c == a
+// (2) Sign(b,c,a) == Sign(a,b,c) for all a,b,c
+// (3) Sign(c,b,a) == -Sign(a,b,c) for all a,b,c
+//
+// In other words:
+//
+// (1) The result is zero if and only if two points are the same.
+// (2) Rotating the order of the arguments does not affect the result.
+// (3) Exchanging any two arguments inverts the result.
+//
+// On the other hand, note that it is not true in general that
+// Sign(-a,b,c) == -Sign(a,b,c), or any similar identities
+// involving antipodal points.
+int Sign(const S2Point& a, const S2Point& b, const S2Point& c);
+
+// Given 4 points on the unit sphere, return true if the edges OA, OB, and
+// OC are encountered in that order while sweeping CCW around the point O.
+// You can think of this as testing whether A <= B <= C with respect to the
+// CCW ordering around O that starts at A, or equivalently, whether B is
+// contained in the range of angles (inclusive) that starts at A and extends
+// CCW to C. Properties:
+//
+// (1) If OrderedCCW(a,b,c,o) && OrderedCCW(b,a,c,o), then a == b
+// (2) If OrderedCCW(a,b,c,o) && OrderedCCW(a,c,b,o), then b == c
+// (3) If OrderedCCW(a,b,c,o) && OrderedCCW(c,b,a,o), then a == b == c
+// (4) If a == b or b == c, then OrderedCCW(a,b,c,o) is true
+// (5) Otherwise if a == c, then OrderedCCW(a,b,c,o) is false
+bool OrderedCCW(const S2Point& a, const S2Point& b, const S2Point& c,
+ const S2Point& o);
+
+// Returns -1, 0, or +1 according to whether AX < BX, A == B, or AX > BX
+// respectively. Distances are measured with respect to the positions of X,
+// A, and B as though they were reprojected to lie exactly on the surface of
+// the unit sphere. Furthermore, this method uses symbolic perturbations to
+// ensure that the result is non-zero whenever A != B, even when AX == BX
+// exactly, or even when A and B project to the same point on the sphere.
+// Such results are guaranteed to be self-consistent, i.e. if AB < BC and
+// BC < AC, then AB < AC.
+int CompareDistances(const S2Point& x, const S2Point& a, const S2Point& b);
+
+// Returns -1, 0, or +1 according to whether the distance XY is less than,
+// equal to, or greater than "r" respectively. Distances are measured with
+// respect the positions of all points as though they are projected to lie
+// exactly on the surface of the unit sphere.
+int CompareDistance(const S2Point& x, const S2Point& y, S1ChordAngle r);
+
+// Returns -1, 0, or +1 according to whether the distance from the point X to
+// the edge A is less than, equal to, or greater than "r" respectively.
+// Distances are measured with respect the positions of all points as though
+// they were projected to lie exactly on the surface of the unit sphere.
+//
+// REQUIRES: A0 and A1 do not project to antipodal points (e.g., A0 == -A1).
+// This requires that (A0 != C * A1) for any constant C < 0.
+//
+// NOTE(ericv): All of the predicates defined here could be extended to handle
+// edges consisting of antipodal points by implementing additional symbolic
+// perturbation logic (similar to Sign) in order to rigorously define the
+// direction of such edges.
+int CompareEdgeDistance(const S2Point& x, const S2Point& a0, const S2Point& a1,
+ S1ChordAngle r);
+
+// Returns -1, 0, or +1 according to whether the normal of edge A has
+// negative, zero, or positive dot product with the normal of edge B. This
+// essentially measures whether the edges A and B are closer to proceeding in
+// the same direction or in opposite directions around the sphere.
+//
+// This method returns an exact result, i.e. the result is zero if and only if
+// the two edges are exactly perpendicular or at least one edge is degenerate.
+// (i.e., both edge endpoints project to the same point on the sphere).
+//
+// CAVEAT: This method does not use symbolic perturbations. Therefore it can
+// return zero even when A0 != A1 and B0 != B1, e.g. if (A0 == C * A1) exactly
+// for some constant C > 0 (which is possible even when both points are
+// considered "normalized").
+//
+// REQUIRES: Neither edge can consist of antipodal points (e.g., A0 == -A1)
+// (see comments in CompareEdgeDistance).
+int CompareEdgeDirections(const S2Point& a0, const S2Point& a1,
+ const S2Point& b0, const S2Point& b1);
+
+// Returns Sign(X0, X1, Z) where Z is the circumcenter of triangle ABC.
+// The return value is +1 if Z is to the left of edge X, and -1 if Z is to the
+// right of edge X. The return value is zero if A == B, B == C, or C == A
+// (exactly), and also if X0 and X1 project to identical points on the sphere
+// (e.g., X0 == X1).
+//
+// The result is determined with respect to the positions of all points as
+// though they were projected to lie exactly on the surface of the unit
+// sphere. Furthermore this method uses symbolic perturbations to compute a
+// consistent non-zero result even when Z lies exactly on edge X.
+//
+// REQUIRES: X0 and X1 do not project to antipodal points (e.g., X0 == -X1)
+// (see comments in CompareEdgeDistance).
+int EdgeCircumcenterSign(const S2Point& x0, const S2Point& x1,
+ const S2Point& a, const S2Point& b, const S2Point& c);
+
+// This is a specialized method that is used to compute the intersection of an
+// edge X with the Voronoi diagram of a set of points, where each Voronoi
+// region is intersected with a disc of fixed radius "r".
+//
+// Given two sites A and B and an edge (X0, X1) such that d(A,X0) < d(B,X0)
+// and both sites are within the given distance "r" of edge X, this method
+// intersects the Voronoi region of each site with a disc of radius r and
+// determines whether either region has an empty intersection with edge X. It
+// returns FIRST if site A has an empty intersection, SECOND if site B has an
+// empty intersection, NEITHER if neither site has an empty intersection, or
+// UNCERTAIN if A == B exactly. Note that it is not possible for both
+// intersections to be empty because of the requirement that both sites are
+// within distance r of edge X. (For example, the only reason that Voronoi
+// region A can have an empty intersection with X is that site B is closer to
+// all points on X that are within radius r of site A.)
+//
+// The result is determined with respect to the positions of all points as
+// though they were projected to lie exactly on the surface of the unit
+// sphere. Furthermore this method uses symbolic perturbations to compute a
+// consistent non-zero result even when A and B lie on opposite sides of X
+// such that the Voronoi edge between them exactly coincides with edge X, or
+// when A and B are distinct but project to the same point on the sphere
+// (i.e., they are linearly dependent).
+//
+// REQUIRES: r < S1ChordAngle::Right() (90 degrees)
+// REQUIRES: s2pred::CompareDistances(x0, a, b) < 0
+// REQUIRES: s2pred::CompareEdgeDistance(a, x0, x1, r) <= 0
+// REQUIRES: s2pred::CompareEdgeDistance(b, x0, x1, r) <= 0
+// REQUIRES: X0 and X1 do not project to antipodal points (e.g., X0 == -X1)
+// (see comments in CompareEdgeDistance).
+enum class Excluded { FIRST, SECOND, NEITHER, UNCERTAIN };
+std::ostream& operator<<(std::ostream& os, Excluded excluded);
+Excluded GetVoronoiSiteExclusion(const S2Point& a, const S2Point& b,
+ const S2Point& x0, const S2Point& x1,
+ S1ChordAngle r);
+
+/////////////////////////// Low-Level Methods ////////////////////////////
+//
+// Most clients will not need the following methods. They can be slightly
+// more efficient but are harder to use, since they require the client to do
+// all the actual crossing tests.
+
+// A more efficient version of Sign that allows the precomputed
+// cross-product of A and B to be specified. (Unlike the 3 argument
+// version this method is also inlined.)
+inline int Sign(const S2Point& a, const S2Point& b, const S2Point& c,
+ const Vector3_d& a_cross_b);
+
+// This version of Sign returns +1 if the points are definitely CCW, -1 if
+// they are definitely CW, and 0 if two points are identical or the result
+// is uncertain. Uncertain cases can be resolved, if desired, by calling
+// ExpensiveSign.
+//
+// The purpose of this method is to allow additional cheap tests to be done,
+// where possible, in order to avoid calling ExpensiveSign unnecessarily.
+inline int TriageSign(const S2Point& a, const S2Point& b,
+ const S2Point& c, const Vector3_d& a_cross_b);
+
+// This function is invoked by Sign() if the sign of the determinant is
+// uncertain. It always returns a non-zero result unless two of the input
+// points are the same. It uses a combination of multiple-precision
+// arithmetic and symbolic perturbations to ensure that its results are
+// always self-consistent (cf. Simulation of Simplicity, Edelsbrunner and
+// Muecke). The basic idea is to assign an infinitesimal symbolic
+// perturbation to every possible S2Point such that no three S2Points are
+// collinear and no four S2Points are coplanar. These perturbations are so
+// small that they do not affect the sign of any determinant that was
+// non-zero before the perturbations. If "perturb" is false, then instead
+// the exact sign of the unperturbed input points is returned, which can be
+// zero even when all three points are distinct.
+//
+// Unlike Sign(), this method does not require the input points to be
+// normalized.
+int ExpensiveSign(const S2Point& a, const S2Point& b,
+ const S2Point& c, bool perturb = true);
+
+////////////////// Implementation details follow ////////////////////
+
+inline int Sign(const S2Point& a, const S2Point& b, const S2Point& c,
+ const Vector3_d& a_cross_b) {
+ int sign = TriageSign(a, b, c, a_cross_b);
+ if (sign == 0) sign = ExpensiveSign(a, b, c);
+ return sign;
+}
+
+inline int TriageSign(const S2Point& a, const S2Point& b,
+ const S2Point& c, const Vector3_d& a_cross_b) {
+ // kMaxDetError is the maximum error in computing (AxB).C where all vectors
+ // are unit length. Using standard inequalities, it can be shown that
+ //
+ // fl(AxB) = AxB + D where |D| <= (|AxB| + (2/sqrt(3))*|A|*|B|) * e
+ //
+ // where "fl()" denotes a calculation done in floating-point arithmetic,
+ // |x| denotes either absolute value or the L2-norm as appropriate, and
+ // e = 0.5*DBL_EPSILON. Similarly,
+ //
+ // fl(B.C) = B.C + d where |d| <= (1.5*|B.C| + 1.5*|B|*|C|) * e .
+ //
+ // Applying these bounds to the unit-length vectors A,B,C and neglecting
+ // relative error (which does not affect the sign of the result), we get
+ //
+ // fl((AxB).C) = (AxB).C + d where |d| <= (2.5 + 2/sqrt(3)) * e
+ //
+ // which is about 3.6548 * e, or 1.8274 * DBL_EPSILON.
+ const double kMaxDetError = 1.8274 * DBL_EPSILON;
+ S2_DCHECK(S2::IsUnitLength(a));
+ S2_DCHECK(S2::IsUnitLength(b));
+ S2_DCHECK(S2::IsUnitLength(c));
+ double det = a_cross_b.DotProd(c);
+
+ // Double-check borderline cases in debug mode.
+ S2_DCHECK(!FLAGS_s2debug ||
+ std::fabs(det) <= kMaxDetError ||
+ std::fabs(det) >= 100 * kMaxDetError ||
+ det * ExpensiveSign(a, b, c) > 0);
+
+ if (det > kMaxDetError) return 1;
+ if (det < -kMaxDetError) return -1;
+ return 0;
+}
+
+} // namespace s2pred
+
+#endif // S2_S2PREDICATES_H_
--- /dev/null
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// The following functions are not part of the public API. Currently they are
+// only used internally for testing purposes.
+
+#ifndef S2_S2PREDICATES_INTERNAL_H_
+#define S2_S2PREDICATES_INTERNAL_H_
+
+#include <limits>
+
+#include "s2/third_party/absl/base/casts.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s2predicates.h"
+#include "s2/util/math/exactfloat/exactfloat.h"
+#include "s2/util/math/vector.h"
+
+namespace s2pred {
+
+// Returns 2 ** (-digits). This could be implemented using "ldexp" except
+// that std::ldexp is not constexpr in C++11.
+constexpr double epsilon_for_digits(int digits) {
+ return (digits < 64 ? 1.0 / (1ULL << digits) :
+ epsilon_for_digits(digits - 63) / (1ULL << 63));
+}
+
+// Returns the maximum rounding error for arithmetic operations in type T.
+// We could simply return 0.5 * numeric_limits<T>::epsilon(), except that some
+// platforms implement "long double" using "double-double" arithmetic, and for
+// those platforms we need to compute the rounding error manually based on
+// numeric_limits<T>::digits (the number of bits of mantissa precision).
+template <typename T> constexpr T rounding_epsilon() {
+ return epsilon_for_digits(std::numeric_limits<T>::digits);
+}
+
+using Vector3_ld = Vector3<long double>;
+using Vector3_xf = Vector3<ExactFloat>;
+
+inline static Vector3_ld ToLD(const S2Point& x) {
+ return Vector3_ld::Cast(x);
+}
+
+inline static long double ToLD(double x) {
+ return absl::implicit_cast<long double>(x);
+}
+
+inline static Vector3_xf ToExact(const S2Point& x) {
+ return Vector3_xf::Cast(x);
+}
+
+int StableSign(const S2Point& a, const S2Point& b, const S2Point& c);
+
+int ExactSign(const S2Point& a, const S2Point& b, const S2Point& c,
+ bool perturb);
+
+int SymbolicallyPerturbedSign(
+ const Vector3_xf& a, const Vector3_xf& b,
+ const Vector3_xf& c, const Vector3_xf& b_cross_c);
+
+template <class T>
+int TriageCompareCosDistances(const Vector3<T>& x,
+ const Vector3<T>& a, const Vector3<T>& b);
+
+template <class T>
+int TriageCompareSin2Distances(const Vector3<T>& x,
+ const Vector3<T>& a, const Vector3<T>& b);
+
+int ExactCompareDistances(const Vector3_xf& x,
+ const Vector3_xf& a, const Vector3_xf& b);
+
+int SymbolicCompareDistances(const S2Point& x,
+ const S2Point& a, const S2Point& b);
+
+template <class T>
+int TriageCompareSin2Distance(const Vector3<T>& x, const Vector3<T>& y, T r2);
+
+template <class T>
+int TriageCompareCosDistance(const Vector3<T>& x, const Vector3<T>& y, T r2);
+
+int ExactCompareDistance(const Vector3_xf& x, const Vector3_xf& y,
+ const ExactFloat& r2);
+
+template <class T>
+int TriageCompareEdgeDistance(const Vector3<T>& x, const Vector3<T>& a0,
+ const Vector3<T>& a1, T r2);
+
+int ExactCompareEdgeDistance(const S2Point& x, const S2Point& a0,
+ const S2Point& a1, S1ChordAngle r);
+
+template <class T>
+int TriageCompareEdgeDirections(
+ const Vector3<T>& a0, const Vector3<T>& a1,
+ const Vector3<T>& b0, const Vector3<T>& b1);
+
+int ExactCompareEdgeDirections(const Vector3_xf& a0, const Vector3_xf& a1,
+ const Vector3_xf& b0, const Vector3_xf& b1);
+
+template <class T>
+int TriageEdgeCircumcenterSign(const Vector3<T>& x0, const Vector3<T>& x1,
+ const Vector3<T>& a, const Vector3<T>& b,
+ const Vector3<T>& c, int abc_sign);
+
+int ExactEdgeCircumcenterSign(const Vector3_xf& x0, const Vector3_xf& x1,
+ const Vector3_xf& a, const Vector3_xf& b,
+ const Vector3_xf& c, int abc_sign);
+
+int SymbolicEdgeCircumcenterSign(
+ const S2Point& x0, const S2Point& x1,
+ const S2Point& a_arg, const S2Point& b_arg, const S2Point& c_arg);
+
+template <class T>
+Excluded TriageVoronoiSiteExclusion(const Vector3<T>& a, const Vector3<T>& b,
+ const Vector3<T>& x0, const Vector3<T>& x1,
+ T r2);
+
+Excluded ExactVoronoiSiteExclusion(const Vector3_xf& a, const Vector3_xf& b,
+ const Vector3_xf& x0, const Vector3_xf& x1,
+ const ExactFloat& r2);
+} // namespace s2pred
+
+#endif // S2_S2PREDICATES_INTERNAL_H_
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// Defines a few simple map projections. (Clients that need more complex
+// projections should use a third-party library such as GeographicLib to
+// implement their own projection subtypes.)
+
+#ifndef S2_S2PROJECTIONS_H_
+#define S2_S2PROJECTIONS_H_
+
+#include "s2/r2.h"
+#include "s2/s2latlng.h"
+#include "s2/s2point.h"
+
+namespace S2 {
+
+// For the purposes of the S2 library, a projection is a function that maps
+// between S2Points and R2Points. It can also define the coordinate wrapping
+// behavior along each axis.
+class Projection {
+ public:
+ virtual ~Projection() {}
+
+ // Converts a point on the sphere to a projected 2D point.
+ virtual R2Point Project(const S2Point& p) const = 0;
+
+ // Converts a projected 2D point to a point on the sphere.
+ //
+ // If wrapping is defined for a given axis (see below), then this method
+ // should accept any real number for the corresponding coordinate.
+ virtual S2Point Unproject(const R2Point& p) const = 0;
+
+ // Convenience function equivalent to Project(ll.ToPoint()), but the
+ // implementation may be more efficient.
+ virtual R2Point FromLatLng(const S2LatLng& ll) const = 0;
+
+ // Convenience function equivalent to S2LatLng(Unproject(p)), but the
+ // implementation may be more efficient.
+ virtual S2LatLng ToLatLng(const R2Point& p) const = 0;
+
+ // Returns the point obtained by interpolating the given fraction of the
+ // distance along the line from A to B. Almost all projections should
+ // use the default implementation of this method, which simply interpolates
+ // linearly in R2 space. Fractions < 0 or > 1 result in extrapolation
+ // instead.
+ //
+ // The only reason to override this method is if you want edges to be
+ // defined as something other than straight lines in the 2D projected
+ // coordinate system. For example, using a third-party library such as
+ // GeographicLib you could define edges as geodesics over an ellipsoid model
+ // of the Earth. (Note that very few data sets define edges this way.)
+ //
+ // Also note that there is no reason to define a projection where edges are
+ // geodesics over the sphere, because this is the native S2 interpretation.
+ virtual R2Point Interpolate(double f, const R2Point& a, const R2Point& b)
+ const;
+
+ // Defines the coordinate wrapping distance along each axis. If this value
+ // is non-zero for a given axis, the coordinates are assumed to "wrap" with
+ // the given period. For example, if wrap_distance.y() == 360 then (x, y)
+ // and (x, y + 360) should map to the same S2Point.
+ //
+ // This information is used to ensure that edges takes the shortest path
+ // between two given points. For example, if coordinates represent
+ // (latitude, longitude) pairs in degrees and wrap_distance().y() == 360,
+ // then the edge (5:179, 5:-179) would be interpreted as spanning 2 degrees
+ // of longitude rather than 358 degrees.
+ //
+ // If a given axis does not wrap, its wrap_distance should be set to zero.
+ virtual R2Point wrap_distance() const = 0;
+
+ // Helper function that wraps the coordinates of B if necessary in order to
+ // obtain the shortest edge AB. For example, suppose that A = [170, 20],
+ // B = [-170, 20], and the projection wraps so that [x, y] == [x + 360, y].
+ // Then this function would return [190, 20] for point B (reducing the edge
+ // length in the "x" direction from 340 to 20).
+ R2Point WrapDestination(const R2Point& a, const R2Point& b) const;
+};
+
+// PlateCarreeProjection defines the "plate carree" (square plate) projection,
+// which converts points on the sphere to (longitude, latitude) pairs.
+// Coordinates can be scaled so that they represent radians, degrees, etc, but
+// the projection is always centered around (latitude=0, longitude=0).
+//
+// Note that (x, y) coordinates are backwards compared to the usual (latitude,
+// longitude) ordering, in order to match the usual convention for graphs in
+// which "x" is horizontal and "y" is vertical.
+class PlateCarreeProjection final : public Projection {
+ public:
+ // Constructs the plate carree projection where the x coordinates
+ // (longitude) span [-x_scale, x_scale] and the y coordinates (latitude)
+ // span [-x_scale/2, x_scale/2]. For example if x_scale==180 then the x
+ // range is [-180, 180] and the y range is [-90, 90].
+ //
+ // By default coordinates are expressed in radians, i.e. the x range is
+ // [-Pi, Pi] and the y range is [-Pi/2, Pi/2].
+ explicit PlateCarreeProjection(double x_scale = M_PI);
+
+ R2Point Project(const S2Point& p) const override;
+ S2Point Unproject(const R2Point& p) const override;
+ R2Point FromLatLng(const S2LatLng& ll) const override;
+ S2LatLng ToLatLng(const R2Point& p) const override;
+ R2Point wrap_distance() const override;
+
+ private:
+ double x_wrap_;
+ double to_radians_; // Multiplier to convert coordinates to radians.
+ double from_radians_; // Multiplier to convert coordinates from radians.
+};
+
+// MercatorProjection defines the spherical Mercator projection. Google Maps
+// uses this projection together with WGS84 coordinates, in which case it is
+// known as the "Web Mercator" projection (see Wikipedia). This class makes
+// no assumptions regarding the coordinate system of its input points, but
+// simply applies the spherical Mercator projection to them.
+//
+// The Mercator projection is finite in width (x) but infinite in height (y).
+// "x" corresponds to longitude, and spans a finite range such as [-180, 180]
+// (with coordinate wrapping), while "y" is a function of latitude and spans
+// an infinite range. (As "y" coordinates get larger, points get closer to
+// the north pole but never quite reach it.) The north and south poles have
+// infinite "y" values. (Note that this will cause problems if you tessellate
+// a Mercator edge where one endpoint is a pole. If you need to do this, clip
+// the edge first so that the "y" coordinate is no more than about 5 * max_x.)
+class MercatorProjection final : public Projection {
+ public:
+ // Constructs a Mercator projection where "x" corresponds to longitude in
+ // the range [-max_x, max_x] , and "y" corresponds to latitude and can be
+ // any real number. The horizontal and vertical scales are equal locally.
+ explicit MercatorProjection(double max_x);
+
+ R2Point Project(const S2Point& p) const override;
+ S2Point Unproject(const R2Point& p) const override;
+ R2Point FromLatLng(const S2LatLng& ll) const override;
+ S2LatLng ToLatLng(const R2Point& p) const override;
+ R2Point wrap_distance() const override;
+
+ private:
+ double x_wrap_;
+ double to_radians_; // Multiplier to convert coordinates to radians.
+ double from_radians_; // Multiplier to convert coordinates from radians.
+};
+
+} // namespace S2
+
+
+#endif // S2_S2PROJECTIONS_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2R2RECT_H_
+#define S2_S2R2RECT_H_
+
+#include <iosfwd>
+
+#include "s2/base/logging.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/r1interval.h"
+#include "s2/r2.h"
+#include "s2/r2rect.h"
+#include "s2/s1angle.h"
+#include "s2/s2region.h"
+
+class Decoder;
+class Encoder;
+class R1Interval;
+class S2Cap;
+class S2Cell;
+class S2CellId;
+class S2LatLngRect;
+
+// This class is a stopgap measure that allows some of the S2 spherical
+// geometry machinery to be applied to planar geometry. An S2R2Rect
+// represents a closed axis-aligned rectangle in the (x,y) plane (an R2Rect),
+// but it also happens to be a subtype of S2Region, which means that you can
+// use an S2RegionCoverer to approximate it as a collection of S2CellIds.
+//
+// With respect to the S2Cell decomposition, an S2R2Rect is interpreted as a
+// region of (s,t)-space on face 0. In particular, the rectangle [0,1]x[0,1]
+// corresponds to the S2CellId that covers all of face 0. This means that
+// only rectangles that are subsets of [0,1]x[0,1] can be approximated using
+// the S2RegionCoverer interface.
+//
+// The S2R2Rect class is also a convenient way to find the (s,t)-region
+// covered by a given S2CellId (see the FromCell and FromCellId methods).
+//
+// TODO(ericv): If the geometry library is extended to have better support
+// for planar geometry, then this class should no longer be necessary.
+//
+// This class is intended to be copied by value as desired. It uses
+// the default copy constructor and assignment operator, however it is
+// not a "plain old datatype" (POD) because it has virtual functions.
+class S2R2Rect final : public S2Region {
+ public:
+ // Construct a rectangle from an R2Rect.
+ explicit S2R2Rect(const R2Rect& rect);
+
+ // Construct a rectangle from the given lower-left and upper-right points.
+ S2R2Rect(const R2Point& lo, const R2Point& hi);
+
+ // Construct a rectangle from the given intervals in x and y. The two
+ // intervals must either be both empty or both non-empty.
+ S2R2Rect(const R1Interval& x, const R1Interval& y);
+
+ // The canonical empty rectangle. Use is_empty() to test for empty
+ // rectangles, since they have more than one representation.
+ static S2R2Rect Empty();
+
+ // Construct a rectangle that corresponds to the boundary of the given cell
+ // is (s,t)-space. Such rectangles are always a subset of [0,1]x[0,1].
+ static S2R2Rect FromCell(const S2Cell& cell);
+ static S2R2Rect FromCellId(S2CellId id);
+
+ // Construct a rectangle from a center point and size in each dimension.
+ // Both components of size should be non-negative, i.e. this method cannot
+ // be used to create an empty rectangle.
+ static S2R2Rect FromCenterSize(const R2Point& center, const R2Point& size);
+
+ // Convenience method to construct a rectangle containing a single point.
+ static S2R2Rect FromPoint(const R2Point& p);
+
+ // Convenience method to construct the minimal bounding rectangle containing
+ // the two given points. This is equivalent to starting with an empty
+ // rectangle and calling AddPoint() twice. Note that it is different than
+ // the S2R2Rect(lo, hi) constructor, where the first point is always
+ // used as the lower-left corner of the resulting rectangle.
+ static S2R2Rect FromPointPair(const R2Point& p1, const R2Point& p2);
+
+ // Accessor methods.
+ const R1Interval& x() const;
+ const R1Interval& y() const;
+ R2Point lo() const;
+ R2Point hi() const;
+
+ // Methods that allow the S2R2Rect to be accessed as a vector.
+ const R1Interval& operator[](int i) const;
+ R1Interval& operator[](int i);
+
+ // Return true if the rectangle is valid, which essentially just means
+ // that if the bound for either axis is empty then both must be.
+ bool is_valid() const;
+
+ // Return true if the rectangle is empty, i.e. it contains no points at all.
+ bool is_empty() const;
+
+ // Return the k-th vertex of the rectangle (k = 0,1,2,3) in CCW order.
+ // Vertex 0 is in the lower-left corner. For convenience, the argument is
+ // reduced modulo 4 to the range [0..3].
+ R2Point GetVertex(int k) const;
+
+ // Return the vertex in direction "i" along the x-axis (0=left, 1=right) and
+ // direction "j" along the y-axis (0=down, 1=up). Equivalently, return the
+ // vertex constructed by selecting endpoint "i" of the x-interval (0=lo,
+ // 1=hi) and vertex "j" of the y-interval.
+ R2Point GetVertex(int i, int j) const;
+
+ // Return the center of the rectangle in (x,y)-space
+ // (in general this is not the center of the region on the sphere).
+ R2Point GetCenter() const;
+
+ // Return the width and height of this rectangle in (x,y)-space. Empty
+ // rectangles have a negative width and height.
+ R2Point GetSize() const;
+
+ // Return true if the rectangle contains the given point. Note that
+ // rectangles are closed regions, i.e. they contain their boundary.
+ bool Contains(const R2Point& p) const;
+
+ // Return true if and only if the given point is contained in the interior
+ // of the region (i.e. the region excluding its boundary).
+ bool InteriorContains(const R2Point& p) const;
+
+ // Return true if and only if the rectangle contains the given other
+ // rectangle.
+ bool Contains(const S2R2Rect& other) const;
+
+ // Return true if and only if the interior of this rectangle contains all
+ // points of the given other rectangle (including its boundary).
+ bool InteriorContains(const S2R2Rect& other) const;
+
+ // Return true if this rectangle and the given other rectangle have any
+ // points in common.
+ bool Intersects(const S2R2Rect& other) const;
+
+ // Return true if and only if the interior of this rectangle intersects
+ // any point (including the boundary) of the given other rectangle.
+ bool InteriorIntersects(const S2R2Rect& other) const;
+
+ // Increase the size of the bounding rectangle to include the given point.
+ // The rectangle is expanded by the minimum amount possible.
+ void AddPoint(const R2Point& p);
+
+ // Return the closest point in the rectangle to the given point "p".
+ // The rectangle must be non-empty.
+ R2Point Project(const R2Point& p) const;
+
+ // Return a rectangle that has been expanded on each side in the x-direction
+ // by margin.x(), and on each side in the y-direction by margin.y(). If
+ // either margin is negative, then shrink the interval on the corresponding
+ // sides instead. The resulting rectangle may be empty. Any expansion of
+ // an empty rectangle remains empty.
+ S2R2Rect Expanded(const R2Point& margin) const;
+ S2R2Rect Expanded(double margin) const;
+
+ // Return the smallest rectangle containing the union of this rectangle and
+ // the given rectangle.
+ S2R2Rect Union(const S2R2Rect& other) const;
+
+ // Return the smallest rectangle containing the intersection of this
+ // rectangle and the given rectangle.
+ S2R2Rect Intersection(const S2R2Rect& other) const;
+
+ // Return true if two rectangles contains the same set of points.
+ bool operator==(const S2R2Rect& other) const;
+
+ // Return true if the x- and y-intervals of the two rectangles are the same
+ // up to the given tolerance (see r1interval.h for details).
+ bool ApproxEquals(const S2R2Rect& other,
+ S1Angle max_error = S1Angle::Radians(1e-15)) const;
+
+ // Return the unit-length S2Point corresponding to the given point "p" in
+ // the (s,t)-plane. "p" need not be restricted to the range [0,1]x[0,1].
+ static S2Point ToS2Point(const R2Point& p);
+
+ ////////////////////////////////////////////////////////////////////////
+ // S2Region interface (see s2region.h for details):
+
+ S2R2Rect* Clone() const override;
+ S2Cap GetCapBound() const override;
+ S2LatLngRect GetRectBound() const override;
+ bool Contains(const S2Point& p) const override;
+ bool Contains(const S2Cell& cell) const override;
+ bool MayIntersect(const S2Cell& cell) const override;
+
+ private:
+ R2Rect rect_;
+};
+
+std::ostream& operator<<(std::ostream& os, const S2R2Rect& r);
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline S2R2Rect::S2R2Rect(const R2Rect& rect) : rect_(rect) {}
+
+inline S2R2Rect::S2R2Rect(const R2Point& lo, const R2Point& hi)
+ : rect_(lo, hi) {}
+
+inline S2R2Rect::S2R2Rect(const R1Interval& x, const R1Interval& y)
+ : rect_(x, y) {}
+
+inline S2R2Rect S2R2Rect::FromCenterSize(const R2Point& center,
+ const R2Point& size) {
+ return S2R2Rect(R2Rect::FromCenterSize(center, size));
+}
+
+inline S2R2Rect S2R2Rect::FromPoint(const R2Point& p) {
+ return S2R2Rect(R2Rect::FromPoint(p));
+}
+
+inline S2R2Rect S2R2Rect::FromPointPair(const R2Point& p1, const R2Point& p2) {
+ return S2R2Rect(R2Rect::FromPointPair(p1, p2));
+}
+
+inline const R1Interval& S2R2Rect::x() const { return rect_.x(); }
+inline const R1Interval& S2R2Rect::y() const { return rect_.y(); }
+inline R2Point S2R2Rect::lo() const { return rect_.lo(); }
+inline R2Point S2R2Rect::hi() const { return rect_.hi(); }
+inline const R1Interval& S2R2Rect::operator[](int i) const { return rect_[i]; }
+inline R1Interval& S2R2Rect::operator[](int i) { return rect_[i]; }
+inline S2R2Rect S2R2Rect::Empty() { return S2R2Rect(R2Rect::Empty()); }
+inline bool S2R2Rect::is_valid() const { return rect_.is_valid(); }
+inline bool S2R2Rect::is_empty() const { return rect_.is_empty(); }
+inline R2Point S2R2Rect::GetVertex(int k) const { return rect_.GetVertex(k); }
+inline R2Point S2R2Rect::GetVertex(int i, int j) const {
+ return rect_.GetVertex(i, j);
+}
+inline R2Point S2R2Rect::GetCenter() const { return rect_.GetCenter(); }
+inline R2Point S2R2Rect::GetSize() const { return rect_.GetSize(); }
+inline bool S2R2Rect::Contains(const R2Point& p) const {
+ return rect_.Contains(p);
+}
+inline bool S2R2Rect::InteriorContains(const R2Point& p) const {
+ return rect_.InteriorContains(p);
+}
+inline bool S2R2Rect::Contains(const S2R2Rect& other) const {
+ return rect_.Contains(other.rect_);
+}
+inline bool S2R2Rect::InteriorContains(const S2R2Rect& other) const {
+ return rect_.InteriorContains(other.rect_);
+}
+inline bool S2R2Rect::Intersects(const S2R2Rect& other) const {
+ return rect_.Intersects(other.rect_);
+}
+inline bool S2R2Rect::InteriorIntersects(const S2R2Rect& other) const {
+ return rect_.InteriorIntersects(other.rect_);
+}
+inline void S2R2Rect::AddPoint(const R2Point& p) {
+ rect_.AddPoint(p);
+}
+inline R2Point S2R2Rect::Project(const R2Point& p) const {
+ return rect_.Project(p);
+}
+inline S2R2Rect S2R2Rect::Expanded(const R2Point& margin) const {
+ return S2R2Rect(rect_.Expanded(margin));
+}
+inline S2R2Rect S2R2Rect::Expanded(double margin) const {
+ return S2R2Rect(rect_.Expanded(margin));
+}
+inline S2R2Rect S2R2Rect::Union(const S2R2Rect& other) const {
+ return S2R2Rect(rect_.Union(other.rect_));
+}
+inline S2R2Rect S2R2Rect::Intersection(const S2R2Rect& other) const {
+ return S2R2Rect(rect_.Intersection(other.rect_));
+}
+inline bool S2R2Rect::operator==(const S2R2Rect& other) const {
+ return rect_ == other.rect_;
+}
+inline bool S2R2Rect::ApproxEquals(const S2R2Rect& other,
+ S1Angle max_error) const {
+ return rect_.ApproxEquals(other.rect_, max_error.radians());
+}
+
+#endif // S2_S2R2RECT_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2REGION_H_
+#define S2_S2REGION_H_
+
+#include <vector>
+
+#include "s2/_fp_contract_off.h"
+#include "s2/s1angle.h"
+
+class Decoder;
+class Encoder;
+
+class S2Cap;
+class S2Cell;
+class S2CellId;
+class S2LatLngRect;
+
+// An S2Region represents a two-dimensional region over the unit sphere.
+// It is an abstract interface with various concrete subtypes.
+//
+// The main purpose of this interface is to allow complex regions to be
+// approximated as simpler regions. So rather than having a wide variety
+// of virtual methods that are implemented by all subtypes, the interface
+// is restricted to methods that are useful for computing approximations.
+class S2Region {
+ public:
+ S2Region() = default;
+ S2Region(const S2Region& other) = default;
+ S2Region& operator=(const S2Region&) = default;
+ virtual ~S2Region() {}
+
+ // Returns a deep copy of the region.
+ //
+ // Note that each subtype of S2Region returns a pointer to an object of its
+ // own type (e.g., S2Cap::Clone() returns an S2Cap*).
+ virtual S2Region* Clone() const = 0;
+
+ // Returns a bounding spherical cap that contains the region. The bound may
+ // not be tight.
+ virtual S2Cap GetCapBound() const = 0;
+
+ // Returns a bounding latitude-longitude rectangle that contains the region.
+ // The bound may not be tight.
+ virtual S2LatLngRect GetRectBound() const = 0;
+
+ // Returns a small collection of S2CellIds whose union covers the region.
+ // The cells are not sorted, may have redundancies (such as cells that
+ // contain other cells), and may cover much more area than necessary.
+ //
+ // This method is not intended for direct use by client code. Clients
+ // should typically use S2RegionCoverer::GetCovering, which has options to
+ // control the size and accuracy of the covering. Alternatively, if you
+ // want a fast covering and don't care about accuracy, consider calling
+ // S2RegionCoverer::GetFastCovering (which returns a cleaned-up version of
+ // the covering computed by this method).
+ //
+ // GetCellUnionBound() implementations should attempt to return a small
+ // covering (ideally 4 cells or fewer) that covers the region and can be
+ // computed quickly. The result is used by S2RegionCoverer as a starting
+ // point for further refinement.
+ //
+ // TODO(ericv): Remove the default implementation.
+ virtual void GetCellUnionBound(std::vector<S2CellId> *cell_ids) const;
+
+ // Returns true if the region completely contains the given cell, otherwise
+ // returns false.
+ virtual bool Contains(const S2Cell& cell) const = 0;
+
+ // If this method returns false, the region does not intersect the given
+ // cell. Otherwise, either region intersects the cell, or the intersection
+ // relationship could not be determined.
+ //
+ // Note that there is currently exactly one implementation of this method
+ // (S2LatLngRect::MayIntersect) that takes advantage of the semantics above
+ // to be more efficient. For all other S2Region subtypes, this method
+ // returns true if the region intersect the cell and false otherwise.
+ virtual bool MayIntersect(const S2Cell& cell) const = 0;
+
+ // Returns true if and only if the given point is contained by the region.
+ // The point 'p' is generally required to be unit length, although some
+ // subtypes may relax this restriction.
+ virtual bool Contains(const S2Point& p) const = 0;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Many S2Region subtypes also define the following non-virtual methods.
+ //////////////////////////////////////////////////////////////////////////
+
+ // Appends a serialized representation of the region to "encoder".
+ //
+ // The representation chosen is left up to the sub-classes but it should
+ // satisfy the following constraints:
+ // - It should encode a version number.
+ // - It should be deserializable using the corresponding Decode method.
+ // - Performance, not space, should be the chief consideration. Encode() and
+ // Decode() should be implemented such that the combination is equivalent
+ // to calling Clone().
+ //
+ // REQUIRES: "encoder" uses the default constructor, so that its buffer
+ // can be enlarged as necessary by calling Ensure(int).
+ //
+ // void Encode(Encoder* const encoder) const;
+
+ // Decodes an S2Region encoded with Encode(). Note that this method
+ // requires that an S2Region object of the appropriate concrete type has
+ // already been constructed. It is not possible to decode regions of
+ // unknown type.
+ //
+ // Whenever the Decode method is changed to deal with new serialized
+ // representations, it should be done so in a manner that allows for
+ // older versions to be decoded i.e. the version number in the serialized
+ // representation should be used to decide how to decode the data.
+ //
+ // Returns true on success.
+ //
+ // bool Decode(Decoder* const decoder);
+
+ // Provides the same functionality as Decode, except that decoded regions
+ // are allowed to point directly into the Decoder's memory buffer rather
+ // than copying the data. This method can be much faster for regions that
+ // have a lot of data (such as polygons), but the decoded region is only
+ // valid within the scope (lifetime) of the Decoder's memory buffer.
+ //
+ // bool DecodeWithinScope(Decoder* const decoder);
+};
+
+#endif // S2_S2REGION_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2REGION_COVERER_H_
+#define S2_S2REGION_COVERER_H_
+
+#include <cstddef>
+#include <new>
+#include <queue>
+#include <utility>
+#include <vector>
+
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/s2cell.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2cell_union.h"
+
+class S2Region;
+
+// An S2RegionCoverer is a class that allows arbitrary regions to be
+// approximated as unions of cells (S2CellUnion). This is useful for
+// implementing various sorts of search and precomputation operations.
+//
+// Typical usage:
+//
+// S2RegionCoverer::Options options;
+// options.set_max_cells(5);
+// S2RegionCoverer coverer(options);
+// S2Cap cap(center, radius);
+// S2CellUnion covering = coverer.GetCovering(cap);
+//
+// This yields a vector of at most 5 cells that is guaranteed to cover the
+// given cap (a disc-shaped region on the sphere).
+//
+// The approximation algorithm is not optimal but does a pretty good job in
+// practice. The output does not always use the maximum number of cells
+// allowed, both because this would not always yield a better approximation,
+// and because max_cells() is a limit on how much work is done exploring the
+// possible covering as well as a limit on the final output size.
+//
+// Because it is an approximation algorithm, one should not rely on the
+// stability of the output. In particular, the output of the covering algorithm
+// may change across different versions of the library.
+//
+// One can also generate interior coverings, which are sets of cells which
+// are entirely contained within a region. Interior coverings can be
+// empty, even for non-empty regions, if there are no cells that satisfy
+// the provided constraints and are contained by the region. Note that for
+// performance reasons, it is wise to specify a max_level when computing
+// interior coverings - otherwise for regions with small or zero area, the
+// algorithm may spend a lot of time subdividing cells all the way to leaf
+// level to try to find contained cells.
+class S2RegionCoverer {
+ public:
+#ifndef SWIG
+ class Options {
+ public:
+ // Sets the desired maximum number of cells in the approximation. Note
+ // the following:
+ //
+ // - For any setting of max_cells(), up to 6 cells may be returned if
+ // that is the minimum number required (e.g. if the region intersects
+ // all six cube faces). Even for very tiny regions, up to 3 cells may
+ // be returned if they happen to be located at the intersection of
+ // three cube faces.
+ //
+ // - min_level() takes priority over max_cells(), i.e. cells below the
+ // given level will never be used even if this causes a large number of
+ // cells to be returned.
+ //
+ // - If max_cells() is less than 4, the area of the covering may be
+ // arbitrarily large compared to the area of the original region even
+ // if the region is convex (e.g. an S2Cap or S2LatLngRect).
+ //
+ // Accuracy is measured by dividing the area of the covering by the area
+ // of the original region. The following table shows the median and worst
+ // case values for this area ratio on a test case consisting of 100,000
+ // spherical caps of random size (generated using s2region_coverer_test):
+ //
+ // max_cells: 3 4 5 6 8 12 20 100 1000
+ // median ratio: 5.33 3.32 2.73 2.34 1.98 1.66 1.42 1.11 1.01
+ // worst case: 215518 14.41 9.72 5.26 3.91 2.75 1.92 1.20 1.02
+ //
+ // The default value of 8 gives a reasonable tradeoff between the number
+ // of cells used and the accuracy of the approximation.
+ //
+ // DEFAULT: kDefaultMaxCells
+ static constexpr int kDefaultMaxCells = 8;
+ int max_cells() const { return max_cells_; }
+ void set_max_cells(int max_cells);
+
+ // Sets the minimum and maximum cell levels to be used. The default is to
+ // use all cell levels.
+ //
+ // To find the cell level corresponding to a given physical distance, use
+ // the S2Cell metrics defined in s2metrics.h. For example, to find the
+ // cell level that corresponds to an average edge length of 10km, use:
+ //
+ // int level =
+ // S2::kAvgEdge.GetClosestLevel(S2Earth::KmToRadians(length_km));
+ //
+ // Note that min_level() takes priority over max_cells(), i.e. cells below
+ // the given level will never be used even if this causes a large number
+ // of cells to be returned. (This doesn't apply to interior coverings,
+ // since interior coverings make no completeness guarantees -- the result
+ // is simply a set of cells that covers as much of the interior as
+ // possible while satisfying the given restrictions.)
+ //
+ // REQUIRES: min_level() <= max_level()
+ // DEFAULT: 0
+ int min_level() const { return min_level_; }
+ void set_min_level(int min_level);
+
+ // REQUIRES: min_level() <= max_level()
+ // DEFAULT: S2CellId::kMaxLevel
+ int max_level() const { return max_level_; }
+ void set_max_level(int max_level);
+
+ // Convenience function that sets both the maximum and minimum cell levels.
+ void set_fixed_level(int level);
+
+ // If specified, then only cells where (level - min_level) is a multiple
+ // of "level_mod" will be used (default 1). This effectively allows the
+ // branching factor of the S2CellId hierarchy to be increased. Currently
+ // the only parameter values allowed are 1, 2, or 3, corresponding to
+ // branching factors of 4, 16, and 64 respectively.
+ //
+ // DEFAULT: 1
+ int level_mod() const { return level_mod_; }
+ void set_level_mod(int level_mod);
+
+ // Convenience function that returns the maximum level such that
+ //
+ // (level <= max_level()) && (level - min_level()) % level_mod() == 0.
+ //
+ // This is the maximum level that will actually be used in coverings.
+ int true_max_level() const;
+
+ protected:
+ int max_cells_ = kDefaultMaxCells;
+ int min_level_ = 0;
+ int max_level_ = S2CellId::kMaxLevel;
+ int level_mod_ = 1;
+ };
+
+ // Constructs an S2RegionCoverer with the given options.
+ explicit S2RegionCoverer(const Options& options);
+
+ // S2RegionCoverer is movable but not copyable.
+ S2RegionCoverer(const S2RegionCoverer&) = delete;
+ S2RegionCoverer& operator=(const S2RegionCoverer&) = delete;
+ S2RegionCoverer(S2RegionCoverer&&);
+ S2RegionCoverer& operator=(S2RegionCoverer&&);
+#endif // SWIG
+
+ // Default constructor. Options can be set using mutable_options().
+ S2RegionCoverer();
+ ~S2RegionCoverer();
+
+ // Returns the current options. Options can be modifed between calls.
+ const Options& options() const { return options_; }
+ Options* mutable_options() { return &options_; }
+
+ // Returns an S2CellUnion that covers (GetCovering) or is contained within
+ // (GetInteriorCovering) the given region and satisfies the current options.
+ //
+ // Note that if options().min_level() > 0 or options().level_mod() > 1, the
+ // by definition the S2CellUnion may not be normalized, i.e. there may be
+ // groups of four child cells that can be replaced by their parent cell.
+ S2CellUnion GetCovering(const S2Region& region);
+ S2CellUnion GetInteriorCovering(const S2Region& region);
+
+ // Like the methods above, but works directly with a vector of S2CellIds.
+ // This version can be more efficient when this method is called many times,
+ // since it does not require allocating a new vector on each call.
+ void GetCovering(const S2Region& region, std::vector<S2CellId>* covering);
+ void GetInteriorCovering(const S2Region& region,
+ std::vector<S2CellId>* interior);
+
+ // Like GetCovering(), except that this method is much faster and the
+ // coverings are not as tight. All of the usual parameters are respected
+ // (max_cells, min_level, max_level, and level_mod), except that the
+ // implementation makes no attempt to take advantage of large values of
+ // max_cells(). (A small number of cells will always be returned.)
+ //
+ // This function is useful as a starting point for algorithms that
+ // recursively subdivide cells.
+ void GetFastCovering(const S2Region& region, std::vector<S2CellId>* covering);
+
+ // Given a connected region and a starting point on the boundary or inside the
+ // region, returns a set of cells at the given level that cover the region.
+ // The output cells are returned in arbitrary order.
+ //
+ // Note that this method is *not* faster than the regular GetCovering()
+ // method for most region types, such as S2Cap or S2Polygon, and in fact it
+ // can be much slower when the output consists of a large number of cells.
+ // Currently it can be faster at generating coverings of long narrow regions
+ // such as polylines, but this may change in the future, in which case this
+ // method will most likely be removed.
+ static void GetSimpleCovering(const S2Region& region, const S2Point& start,
+ int level, std::vector<S2CellId>* output);
+
+ // Like GetSimpleCovering(), but accepts a starting S2CellId rather than a
+ // starting point and cell level. Returns all edge-connected cells at the
+ // same level as "start" that intersect "region", in arbitrary order.
+ static void FloodFill(const S2Region& region, S2CellId start,
+ std::vector<S2CellId>* output);
+
+ // Returns true if the given S2CellId vector represents a valid covering
+ // that conforms to the current covering parameters. In particular:
+ //
+ // - All S2CellIds must be valid.
+ //
+ // - S2CellIds must be sorted and non-overlapping.
+ //
+ // - S2CellId levels must satisfy min_level(), max_level(), and level_mod().
+ //
+ // - If covering.size() > max_cells(), there must be no two cells with
+ // a common ancestor at min_level() or higher.
+ //
+ // - There must be no sequence of cells that could be replaced by an
+ // ancestor (i.e. with level_mod() == 1, the 4 child cells of a parent).
+ bool IsCanonical(const S2CellUnion& covering) const;
+ bool IsCanonical(const std::vector<S2CellId>& covering) const;
+
+ // Modify "covering" if necessary so that it conforms to the current
+ // covering parameters (max_cells, min_level, max_level, and level_mod).
+ // There are no restrictions on the input S2CellIds (they may be unsorted,
+ // overlapping, etc).
+ S2CellUnion CanonicalizeCovering(const S2CellUnion& covering);
+ void CanonicalizeCovering(std::vector<S2CellId>* covering);
+
+ private:
+ struct Candidate {
+ void* operator new(std::size_t size, std::size_t max_children) {
+ return ::operator new (size + max_children * sizeof(Candidate *));
+ }
+
+ void operator delete(void* p) {
+ ::operator delete (p);
+ }
+
+ Candidate(const S2Cell& cell, const std::size_t max_children)
+ : cell(cell), is_terminal(max_children == 0) {
+ std::fill_n(&children[0], max_children,
+ absl::implicit_cast<Candidate*>(nullptr));
+ }
+
+ // Default destructor is fine; Candidate* is trivially destructible.
+ // children must be deleted by DeleteCandidate.
+ ~Candidate() = default;
+
+ S2Cell cell;
+ bool is_terminal; // Cell should not be expanded further.
+ int num_children = 0; // Number of children that intersect the region.
+ __extension__ Candidate* children[0]; // Actual size may be 0, 4, 16, or 64 elements.
+ };
+
+ // If the cell intersects the given region, return a new candidate with no
+ // children, otherwise return nullptr. Also marks the candidate as "terminal"
+ // if it should not be expanded further.
+ Candidate* NewCandidate(const S2Cell& cell);
+
+ // Returns the log base 2 of the maximum number of children of a candidate.
+ int max_children_shift() const { return 2 * options().level_mod(); }
+
+ // Frees the memory associated with a candidate.
+ static void DeleteCandidate(Candidate* candidate, bool delete_children);
+
+ // Processes a candidate by either adding it to the result_ vector or
+ // expanding its children and inserting it into the priority queue.
+ // Passing an argument of nullptr does nothing.
+ void AddCandidate(Candidate* candidate);
+
+ // Populates the children of "candidate" by expanding the given number of
+ // levels from the given cell. Returns the number of children that were
+ // marked "terminal".
+ int ExpandChildren(Candidate* candidate, const S2Cell& cell, int num_levels);
+
+ // Computes a set of initial candidates that cover the given region.
+ void GetInitialCandidates();
+
+ // Generates a covering and stores it in result_.
+ void GetCoveringInternal(const S2Region& region);
+
+ // If level > min_level(), then reduces "level" if necessary so that it also
+ // satisfies level_mod(). Levels smaller than min_level() are not affected
+ // (since cells at these levels are eventually expanded).
+ int AdjustLevel(int level) const;
+
+ // Ensures that all cells with level > min_level() also satisfy level_mod(),
+ // by replacing them with an ancestor if necessary. Cell levels smaller
+ // than min_level() are not modified (see AdjustLevel). The output is
+ // then normalized to ensure that no redundant cells are present.
+ void AdjustCellLevels(std::vector<S2CellId>* cells) const;
+
+ // Returns true if "covering" contains all children of "id" at level
+ // (id.level() + options_.level_mod()).
+ bool ContainsAllChildren(const std::vector<S2CellId>& covering,
+ S2CellId id) const;
+
+ // Replaces all descendants of "id" in "covering" with "id".
+ // REQUIRES: "covering" contains at least one descendant of "id".
+ void ReplaceCellsWithAncestor(std::vector<S2CellId>* covering,
+ S2CellId id) const;
+
+ Options options_;
+
+ // We save a temporary copy of the pointer passed to GetCovering() in order
+ // to avoid passing this parameter around internally. It is only used (and
+ // only valid) for the duration of a single GetCovering() call.
+ const S2Region* region_ = nullptr;
+
+ // The set of S2CellIds that have been added to the covering so far.
+ std::vector<S2CellId> result_;
+
+ // We keep the candidates in a priority queue. We specify a vector to hold
+ // the queue entries since for some reason priority_queue<> uses a deque by
+ // default. We define our own own comparison function on QueueEntries in
+ // order to make the results deterministic. (Using the default
+ // less<QueueEntry>, entries of equal priority would be sorted according to
+ // the memory address of the candidate.)
+
+ typedef std::pair<int, Candidate*> QueueEntry;
+ struct CompareQueueEntries {
+ bool operator()(const QueueEntry& x, const QueueEntry& y) const {
+ return x.first < y.first;
+ }
+ };
+ typedef std::priority_queue<QueueEntry, std::vector<QueueEntry>,
+ CompareQueueEntries> CandidateQueue;
+ CandidateQueue pq_;
+
+ // True if we're computing an interior covering.
+ bool interior_covering_;
+
+ // Counter of number of candidates created, for performance evaluation.
+ int candidates_created_counter_;
+};
+
+#endif // S2_S2REGION_COVERER_H_
--- /dev/null
+// Copyright 2006 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+
+#ifndef S2_S2REGION_INTERSECTION_H_
+#define S2_S2REGION_INTERSECTION_H_
+
+#include <memory>
+#include <vector>
+
+#include "s2/base/logging.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/s2region.h"
+#include "s2/third_party/absl/base/macros.h"
+
+class Decoder;
+class Encoder;
+class S2Cap;
+class S2Cell;
+class S2LatLngRect;
+
+// An S2RegionIntersection represents the intersection of a set of regions.
+// It is convenient for computing a covering of the intersection of a set of
+// regions.
+class S2RegionIntersection final : public S2Region {
+ public:
+ // Creates an empty intersection that should be initialized by calling Init().
+ // Note: an intersection of no regions covers the entire sphere.
+ S2RegionIntersection() = default;
+
+ // Create a region representing the intersection of the given regions.
+ explicit S2RegionIntersection(std::vector<std::unique_ptr<S2Region>> regions);
+
+ ~S2RegionIntersection() override = default;
+
+ // Initialize region by taking ownership of the given regions.
+ void Init(std::vector<std::unique_ptr<S2Region>> regions);
+
+ // Releases ownership of the regions of this intersection and returns them,
+ // leaving this region empty.
+ std::vector<std::unique_ptr<S2Region>> Release();
+
+ // Accessor methods.
+ int num_regions() const { return regions_.size(); }
+ const S2Region* region(int i) const { return regions_[i].get(); }
+
+ ////////////////////////////////////////////////////////////////////////
+ // S2Region interface (see s2region.h for details):
+
+ S2RegionIntersection* Clone() const override;
+ S2Cap GetCapBound() const override;
+ S2LatLngRect GetRectBound() const override;
+ bool Contains(const S2Point& p) const override;
+ bool Contains(const S2Cell& cell) const override;
+ bool MayIntersect(const S2Cell& cell) const override;
+
+ private:
+ // Internal copy constructor used only by Clone() that makes a deep copy of
+ // its argument.
+ S2RegionIntersection(const S2RegionIntersection& src);
+
+ std::vector<std::unique_ptr<S2Region>> regions_;
+
+ void operator=(const S2RegionIntersection&) = delete;
+};
+
+#endif // S2_S2REGION_INTERSECTION_H_
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// S2RegionTermIndexer is a helper class for adding spatial data to an
+// information retrieval system. Such systems work by converting documents
+// into a collection of "index terms" (e.g., representing words or phrases),
+// and then building an "inverted index" that maps each term to a list of
+// documents (and document positions) where that term occurs.
+//
+// This class deals with the problem of converting spatial data into index
+// terms, which can then be indexed along with the other document information.
+//
+// Spatial data is represented using the S2Region type. Useful S2Region
+// subtypes include:
+//
+// S2Cap
+// - a disc-shaped region
+//
+// S2LatLngRect
+// - a rectangle in latitude-longitude coordinates
+//
+// S2Polyline
+// - a polyline
+//
+// S2Polygon
+// - a polygon, possibly with multiple holes and/or shells
+//
+// S2CellUnion
+// - a region approximated as a collection of S2CellIds
+//
+// S2ShapeIndexRegion
+// - an arbitrary collection of points, polylines, and polygons
+//
+// S2ShapeIndexBufferedRegion
+// - like the above, but expanded by a given radius
+//
+// S2RegionUnion, S2RegionIntersection
+// - the union or intersection of arbitrary other regions
+//
+// So for example, if you want to query documents that are within 500 meters
+// of a polyline, you could use an S2ShapeIndexBufferedRegion containing the
+// polyline with a radius of 500 meters.
+//
+// Example usage:
+//
+// // This class is intended to be used with an external key-value store,
+// // but for this example will we use an unordered_map. The key is an
+// // index term, and the value is a set of document ids.
+// std::unordered_map<string, std::vector<int>> index;
+//
+// // Create an indexer that uses up to 10 cells to approximate each region.
+// S2RegionTermIndexer::Options options;
+// options.set_max_cells(10);
+// S2RegionTermIndexer indexer(options);
+//
+// // For this example, we index a disc-shaped region with a 10km radius.
+// S2LatLng center = S2LatLng::FromDegrees(44.1, -56.235);
+// S1Angle radius = S2Earth::ToAngle(util::units::Kilometers(10.0));
+// S2Cap cap(center.ToPoint(), radius);
+//
+// // Add the terms for this disc-shaped region to the index.
+// for (const auto& term : indexer.GetIndexTerms(cap)) {
+// index[term].push_back(kSomeDocumentId);
+// }
+//
+// // And now at query time: build a latitude-longitude rectangle.
+// S2LatLngRect rect(S2LatLng::FromDegrees(-12.1, 10.2),
+// S2LatLng::FromDegrees(-9.2, 120.5));
+//
+// // Convert the query region to a set of terms, and compute the union
+// // of the document ids associated with those terms.
+// std::set<int> doc_ids;
+// for (const auto& term : indexer.GetQueryTerms(rect)) {
+// doc_ids.insert(index[term].begin(), index[term].end());
+// }
+//
+// // "doc_ids" now contains all documents that intersect the query region,
+// // along with some documents that nearly intersect it. The results can
+// // be further pruned if desired by retrieving the original regions that
+// // were indexed (i.e., the document contents) and checking for exact
+// // intersection with the query region.
+
+#ifndef S2_S2REGION_TERM_INDEXER_H_
+#define S2_S2REGION_TERM_INDEXER_H_
+
+#include <string>
+#include <vector>
+
+#include "s2/s2cell_union.h"
+#include "s2/s2region.h"
+#include "s2/s2region_coverer.h"
+#include "s2/third_party/absl/strings/string_view.h"
+
+class S2RegionTermIndexer {
+ public:
+ // The following parameters control the tradeoffs between index size, query
+ // size, and accuracy (see s2region_coverer.h for details).
+ //
+ // IMPORTANT: You must use the same values for min_level(), max_level(), and
+ // level_mod() for both indexing and queries, otherwise queries will return
+ // incorrect results. However, max_cells() can be changed as often as
+ // desired -- you can even change this parameter for every region.
+
+ class Options : public S2RegionCoverer::Options {
+ public:
+ Options();
+
+ ///////////////// Options Inherited From S2RegionCoverer ////////////////
+
+ // max_cells() controls the maximum number of cells when approximating
+ // each region. This parameter value may be changed as often as desired
+ // (using mutable_options(), see below), e.g. to approximate some regions
+ // more accurately than others.
+ //
+ // Increasing this value during indexing will make indexes more accurate
+ // but larger. Increasing this value for queries will make queries more
+ // accurate but slower. (See s2region_coverer.h for details on how this
+ // parameter affects accuracy.) For example, if you don't mind large
+ // indexes but want fast serving, it might be reasonable to set
+ // max_cells() == 100 during indexing and max_cells() == 8 for queries.
+ //
+ // DEFAULT: 8 (coarse approximations)
+ using S2RegionCoverer::Options::max_cells;
+ using S2RegionCoverer::Options::set_max_cells;
+
+ // min_level() and max_level() control the minimum and maximum size of the
+ // S2Cells used to approximate regions. Setting these parameters
+ // appropriately can reduce the size of the index and speed up queries by
+ // reducing the number of terms needed. For example, if you know that
+ // your query regions will rarely be less than 100 meters in width, then
+ // you could set max_level() as follows:
+ //
+ // options.set_max_level(S2::kAvgEdge.GetClosestLevel(
+ // S2Earth::MetersToRadians(100)));
+ //
+ // This restricts the index to S2Cells that are approximately 100 meters
+ // across or larger. Similar, if you know that query regions will rarely
+ // be larger than 1000km across, then you could set min_level() similarly.
+ //
+ // If min_level() is set too high, then large regions may generate too
+ // many query terms. If max_level() is set too low, then small query
+ // regions will not be able to discriminate which regions they intersect
+ // very precisely and may return many more candidates than necessary.
+ //
+ // If you have no idea about the scale of the regions being queried,
+ // it is perfectly fine to set min_level() == 0 and max_level() == 30
+ // (== S2::kMaxLevel). The only drawback is that may result in a larger
+ // index and slower queries.
+ //
+ // The default parameter values are suitable for query regions ranging
+ // from about 100 meters to 3000 km across.
+ //
+ // DEFAULT: 4 (average cell width == 600km)
+ using S2RegionCoverer::Options::min_level;
+ using S2RegionCoverer::Options::set_min_level;
+
+ // DEFAULT: 16 (average cell width == 150m)
+ using S2RegionCoverer::Options::max_level;
+ using S2RegionCoverer::Options::set_max_level;
+
+ // Setting level_mod() to a value greater than 1 increases the effective
+ // branching factor of the S2Cell hierarchy by skipping some levels. For
+ // example, if level_mod() == 2 then every second level is skipped (which
+ // increases the effective branching factor to 16). You might want to
+ // consider doing this if your query regions are typically very small
+ // (e.g., single points) and you don't mind increasing the index size
+ // (since skipping levels will reduce the accuracy of cell coverings for a
+ // given max_cells() limit).
+ //
+ // DEFAULT: 1 (don't skip any cell levels)
+ using S2RegionCoverer::Options::level_mod;
+ using S2RegionCoverer::Options::set_level_mod;
+
+ // If your index will only contain points (rather than regions), be sure
+ // to set this flag. This will generate smaller and faster queries that
+ // are specialized for the points-only case.
+ //
+ // With the default quality settings, this flag reduces the number of
+ // query terms by about a factor of two. (The improvement gets smaller
+ // as max_cells() is increased, but there is really no reason not to use
+ // this flag if your index consists entirely of points.)
+ //
+ // DEFAULT: false
+ bool index_contains_points_only() const { return points_only_; }
+ void set_index_contains_points_only(bool value) { points_only_ = value; }
+
+ // If true, the index will be optimized for space rather than for query
+ // time. With the default quality settings, this flag reduces the number
+ // of index terms and increases the number of query terms by the same
+ // factor (approximately 1.3). The factor increases up to a limiting
+ // ratio of 2.0 as max_cells() is increased.
+ //
+ // CAVEAT: This option has no effect if the index contains only points.
+ //
+ // DEFAULT: false
+ bool optimize_for_space() const { return optimize_for_space_; }
+ void set_optimize_for_space(bool value) { optimize_for_space_ = value; }
+
+ // A non-alphanumeric character that is used internally to distinguish
+ // between two different types of terms (by adding this character).
+ //
+ // REQUIRES: "ch" is non-alphanumeric.
+ // DEFAULT: '$'
+ const string& marker() const { return marker_; }
+ char marker_character() const { return marker_[0]; }
+ void set_marker_character(char ch);
+
+ private:
+ bool points_only_ = false;
+ bool optimize_for_space_ = false;
+ string marker_ = string(1, '$');
+ };
+
+ // Default constructor. Options can be set using mutable_options().
+ S2RegionTermIndexer();
+ ~S2RegionTermIndexer();
+
+ // Constructs an S2RegionTermIndexer with the given options.
+ explicit S2RegionTermIndexer(const Options& options);
+
+ // S2RegionTermIndexer is movable but not copyable.
+ S2RegionTermIndexer(const S2RegionTermIndexer&) = delete;
+ S2RegionTermIndexer& operator=(const S2RegionTermIndexer&) = delete;
+ S2RegionTermIndexer(S2RegionTermIndexer&&);
+ S2RegionTermIndexer& operator=(S2RegionTermIndexer&&);
+
+ // Returns the current options. Options can be modifed between calls.
+ const Options& options() const { return options_; }
+ Options* mutable_options() { return &options_; }
+
+ // Converts the given region into a set of terms for indexing. Terms
+ // consist of lowercase letters, numbers, '$', and an optional prefix.
+ //
+ // "prefix" is a unique prefix used to distinguish S2 terms from other terms
+ // in the repository. The prefix may also be used to index documents with
+ // multiple types of location information (e.g. store footprint, entrances,
+ // parking lots, etc). The prefix should be kept short since it is
+ // prepended to every term.
+ std::vector<string> GetIndexTerms(const S2Region& region,
+ absl::string_view prefix);
+
+ // Converts a given query region into a set of terms. If you compute the
+ // union of all the documents associated with these terms, the result will
+ // include all documents whose index region intersects the query region.
+ //
+ // "prefix" should match the corresponding value used when indexing.
+ std::vector<string> GetQueryTerms(const S2Region& region,
+ absl::string_view prefix);
+
+ // Convenience methods that accept an S2Point rather than S2Region. (These
+ // methods are also faster.)
+ //
+ // Note that you can index an S2LatLng by converting it to an S2Point first:
+ // auto terms = GetIndexTerms(S2Point(latlng), ...);
+ std::vector<string> GetIndexTerms(const S2Point& point,
+ absl::string_view prefix);
+ std::vector<string> GetQueryTerms(const S2Point& point,
+ absl::string_view prefix);
+
+ // Low-level methods that accept an S2CellUnion covering of the region to be
+ // indexed or queried.
+ //
+ // REQUIRES: "covering" satisfies the S2RegionCoverer::Options for this
+ // class (i.e., max_cells, min_level, max_level, and level_mod).
+ //
+ // If you have a covering that was computed using different options, then
+ // you can either call the regular S2Region methods (since S2CellUnion is a
+ // type of S2Region), or "canonicalize" the covering first by calling
+ // S2RegionCoverer::CanonicalizeCovering() with the same options.
+ std::vector<string> GetIndexTermsForCanonicalCovering(
+ const S2CellUnion& covering, absl::string_view prefix);
+ std::vector<string> GetQueryTermsForCanonicalCovering(
+ const S2CellUnion& covering, absl::string_view prefix);
+
+ private:
+ enum TermType { ANCESTOR, COVERING };
+
+ string GetTerm(TermType term_type, const S2CellId& id,
+ absl::string_view prefix) const;
+
+ Options options_;
+ S2RegionCoverer coverer_;
+};
+
+#endif // S2_S2REGION_TERM_INDEXER_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2REGION_UNION_H_
+#define S2_S2REGION_UNION_H_
+
+#include <memory>
+#include <vector>
+
+#include "s2/base/logging.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/s2region.h"
+#include "s2/third_party/absl/base/macros.h"
+
+class Decoder;
+class Encoder;
+class S2Cap;
+class S2Cell;
+class S2LatLngRect;
+
+// An S2RegionUnion represents a union of possibly overlapping regions.
+// It is convenient for computing a covering of a set of regions.
+class S2RegionUnion final : public S2Region {
+ public:
+ // Create an empty region. Can be made non-empty by calling Init() or Add().
+ S2RegionUnion() = default;
+
+ // Create a region representing the union of the given regions.
+ explicit S2RegionUnion(std::vector<std::unique_ptr<S2Region>> regions);
+
+ // Use {} instead of = default to work around gcc bug.
+ ~S2RegionUnion() override {}
+
+ // Initialize region by taking ownership of the given regions.
+ void Init(std::vector<std::unique_ptr<S2Region>> regions);
+
+ // Releases ownership of the regions of this union and returns them,
+ // leaving this region empty.
+ std::vector<std::unique_ptr<S2Region>> Release();
+
+ // Add the given region to the union. This method can be called repeatedly
+ // as an alternative to Init().
+ void Add(std::unique_ptr<S2Region> region);
+
+ // Accessor methods.
+ int num_regions() const { return regions_.size(); }
+ const S2Region* region(int i) const { return regions_[i].get(); }
+
+ ////////////////////////////////////////////////////////////////////////
+ // S2Region interface (see s2region.h for details):
+
+ S2RegionUnion* Clone() const override;
+ S2Cap GetCapBound() const override;
+ S2LatLngRect GetRectBound() const override;
+ bool Contains(const S2Point& p) const override;
+ bool Contains(const S2Cell& cell) const override;
+ bool MayIntersect(const S2Cell& cell) const override;
+
+ private:
+ // Internal copy constructor used only by Clone() that makes a deep copy of
+ // its argument.
+ S2RegionUnion(const S2RegionUnion& src);
+
+ std::vector<std::unique_ptr<S2Region>> regions_;
+
+ void operator=(const S2RegionUnion&) = delete;
+};
+
+#endif // S2_S2REGION_UNION_H_
--- /dev/null
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2SHAPE_H_
+#define S2_S2SHAPE_H_
+
+#include "s2/base/integral_types.h"
+#include "s2/s2point.h"
+#include "s2/s2pointutil.h"
+
+// The purpose of S2Shape is to represent polygonal geometry in a flexible
+// way. It is organized as a collection of edges that optionally defines an
+// interior. All geometry represented by an S2Shape must have the same
+// dimension, which means that an S2Shape can represent either a set of
+// points, a set of polylines, or a set of polygons.
+//
+// S2Shape is defined as an abstract base class in order to give clients
+// control over the underlying data representation. Sometimes an S2Shape does
+// not have any data of its own, but instead "wraps" some other class. There
+// are various useful subtypes defined in *_shape.h, and some S2 classes also
+// have a nested "Shape" class (e.g., S2Polygon::Shape). It is easy for
+// clients to implement their own subtypes, since the interface is minimal.
+//
+// S2Shape operations are typically defined on S2ShapeIndex objects rather
+// than individual shapes. An S2ShapeIndex is simply a collection of
+// S2Shapes, possibly of different dimensions (e.g. 10 points and 3 polygons),
+// organized into a data structure for efficient edge access.
+//
+// The edges of an S2Shape are identified by a contiguous range of "edge ids"
+// starting at 0. The edges are further subdivided into "chains", where each
+// chain consists of a sequence of edges connected end-to-end (a polyline).
+// For example, an S2Shape representing two polylines AB and CDE would have
+// three edges (AB, CD, DE) grouped into two chains: (AB) and (CD, DE).
+// Similarly, an S2Shape representing 5 points would have 5 chains consisting
+// of one edge each.
+//
+// S2Shape has methods that allow edges to be accessed either using the global
+// numbering (edge id) or within a particular chain. The global numbering is
+// sufficient for most purposes, but the chain representation is useful for
+// certain algorithms such as intersection (see S2BooleanOperation).
+class S2Shape {
+ public:
+ // An edge, consisting of two vertices "v0" and "v1". Zero-length edges are
+ // allowed, and can be used to represent points.
+ struct Edge {
+ S2Point v0, v1;
+ Edge() = default;
+ Edge(const S2Point& _v0, const S2Point& _v1) : v0(_v0), v1(_v1) {}
+
+ // TODO(ericv): Define all 6 comparisons.
+ friend bool operator==(const Edge& x, const Edge& y) {
+ return x.v0 == y.v0 && x.v1 == y.v1;
+ }
+ friend bool operator<(const Edge& x, const Edge& y) {
+ return x.v0 < y.v0 || (x.v0 == y.v0 && x.v1 < y.v1); }
+ };
+
+ // A range of edge ids corresponding to a chain of zero or more connected
+ // edges, specified as a (start, length) pair. The chain is defined to
+ // consist of edge ids {start, start + 1, ..., start + length - 1}.
+ struct Chain {
+ int32 start, length;
+ Chain() = default;
+ Chain(int32 _start, int32 _length) : start(_start), length(_length) {}
+
+ friend bool operator==(const Chain& x, const Chain& y) {
+ return x.start == y.start && x.length == y.length;
+ }
+ };
+
+ // The position of an edge within a given edge chain, specified as a
+ // (chain_id, offset) pair. Chains are numbered sequentially starting from
+ // zero, and offsets are measured from the start of each chain.
+ struct ChainPosition {
+ int32 chain_id, offset;
+ ChainPosition() = default;
+ ChainPosition(int32 _chain_id, int32 _offset)
+ : chain_id(_chain_id), offset(_offset) {}
+
+ friend bool operator==(const ChainPosition& x, const ChainPosition& y) {
+ return x.chain_id == y.chain_id && x.offset == y.offset;
+ }
+ };
+
+ // A ReferencePoint consists of a point P and a boolean indicating whether P
+ // is contained by a particular shape.
+ struct ReferencePoint {
+ S2Point point;
+ bool contained;
+ ReferencePoint() = default;
+ ReferencePoint(S2Point _point, bool _contained)
+ : point(_point), contained(_contained) {}
+
+ // Returns a ReferencePoint with the given "contained" value and a default
+ // "point". It should be used when all points or no points are contained.
+ static ReferencePoint Contained(bool _contained) {
+ return ReferencePoint(S2::Origin(), _contained);
+ }
+
+ friend bool operator==(const ReferencePoint& x, const ReferencePoint& y) {
+ return x.point == y.point && x.contained == y.contained;
+ }
+ };
+
+ // A 32-bit tag that can be used to identify the type of an encoded S2Shape.
+ // All encodable types have a non-zero type tag. The tag associated with a
+ // given shape type can be accessed as Shape::kTypeTag, while the tag
+ // associated with a given object can be accessed as shape.type_tag().
+ //
+ // Type tags in the range 0..8191 are reserved for use by the S2 library.
+ using TypeTag = uint32;
+
+ // Indicates that a given S2Shape type cannot be encoded.
+ static constexpr TypeTag kNoTypeTag = 0;
+
+ // The minimum allowable tag for user-defined S2Shape types.
+ static constexpr TypeTag kMinUserTypeTag = 8192;
+
+ S2Shape() : id_(-1) {}
+ virtual ~S2Shape() {}
+
+ // Returns the number of edges in this shape. Edges have ids ranging from 0
+ // to num_edges() - 1.
+ virtual int num_edges() const = 0;
+
+ // Returns the endpoints of the given edge id.
+ //
+ // REQUIRES: 0 <= id < num_edges()
+ virtual Edge edge(int edge_id) const = 0;
+
+ // Returns the dimension of the geometry represented by this shape.
+ //
+ // 0 - Point geometry. Each point is represented as a degenerate edge.
+ //
+ // 1 - Polyline geometry. Polyline edges may be degenerate. A shape may
+ // represent any number of polylines. Polylines edges may intersect.
+ //
+ // 2 - Polygon geometry. Edges should be oriented such that the polygon
+ // interior is always on the left. In theory the edges may be returned
+ // in any order, but typically the edges are organized as a collection
+ // of edge chains where each chain represents one polygon loop.
+ // Polygons may have degeneracies (e.g., degenerate edges or sibling
+ // pairs consisting of an edge and its corresponding reversed edge).
+ // A polygon loop may also be full (containing all points on the
+ // sphere); by convention this is represented as a chain with no edges.
+ // (See S2LaxPolygonShape for details.)
+ //
+ // Note that this method allows degenerate geometry of different dimensions
+ // to be distinguished, e.g. it allows a point to be distinguished from a
+ // polyline or polygon that has been simplified to a single point.
+ virtual int dimension() const = 0;
+
+ // Returns true if the shape contains no points. (Note that the full
+ // polygon is represented as a chain with zero edges.)
+ bool is_empty() const {
+ return num_edges() == 0 && (dimension() < 2 || num_chains() == 0);
+ }
+ // Returns true if the shape contains all points on the sphere.
+ bool is_full() const {
+ return num_edges() == 0 && dimension() == 2 && num_chains() > 0;
+ }
+
+ // Returns an arbitrary point P along with a boolean indicating whether P is
+ // contained by the shape. (The boolean value must be false for shapes that
+ // do not have an interior.)
+ //
+ // This ReferencePoint may then be used to compute the containment of other
+ // points by counting edge crossings.
+ virtual ReferencePoint GetReferencePoint() const = 0;
+
+ // Returns the number of contiguous edge chains in the shape. For example,
+ // a shape whose edges are [AB, BC, CD, AE, EF] would consist of two chains
+ // (AB,BC,CD and AE,EF). Every chain is assigned a "chain id" numbered
+ // sequentially starting from zero.
+ //
+ // Note that it is always acceptable to implement this method by returning
+ // num_edges() (i.e. every chain consists of a single edge), but this may
+ // reduce the efficiency of some algorithms.
+ virtual int num_chains() const = 0;
+
+ // Returns the range of edge ids corresponding to the given edge chain. The
+ // edge chains must form contiguous, non-overlapping ranges that cover the
+ // entire range of edge ids. This is spelled out more formally below:
+ //
+ // REQUIRES: 0 <= i < num_chains()
+ // REQUIRES: chain(i).length >= 0, for all i
+ // REQUIRES: chain(0).start == 0
+ // REQUIRES: chain(i).start + chain(i).length == chain(i+1).start,
+ // for i < num_chains() - 1
+ // REQUIRES: chain(i).start + chain(i).length == num_edges(),
+ // for i == num_chains() - 1
+ virtual Chain chain(int chain_id) const = 0;
+
+ // Returns the edge at offset "offset" within edge chain "chain_id".
+ // Equivalent to "shape.edge(shape.chain(chain_id).start + offset)"
+ // but may be more efficient.
+ virtual Edge chain_edge(int chain_id, int offset) const = 0;
+
+ // Finds the chain containing the given edge, and returns the position of
+ // that edge as a (chain_id, offset) pair.
+ //
+ // REQUIRES: shape.chain(pos.chain_id).start + pos.offset == edge_id
+ // REQUIRES: shape.chain(pos.chain_id + 1).start > edge_id
+ //
+ // where pos == shape.chain_position(edge_id).
+ virtual ChainPosition chain_position(int edge_id) const = 0;
+
+ // A unique id assigned to this shape by S2ShapeIndex. Shape ids are
+ // assigned sequentially starting from 0 in the order shapes are added.
+ //
+ // TODO(ericv): Consider eliminating this method.
+ int id() const { return id_; }
+
+ // Returns an integer that can be used to identify the type of an encoded
+ // S2Shape (see TypeTag above).
+ virtual TypeTag type_tag() const { return kNoTypeTag; }
+
+ // Virtual methods that return pointers of your choice. These methods are
+ // intended to help with the problem of attaching additional data to S2Shape
+ // objects. For example, you could return a pointer to a source object, or
+ // a pointer to a bundle of additional data allocated with the S2Shape.
+ // Because this method exists in all S2Shapes, you can override it in each
+ // type of shape you have and call it without knowing the concrete subtype.
+ // For example, if you have polyline and polygon shapes, you can do this:
+ //
+ // class MyPolyline : public S2Polyline::Shape {
+ // public:
+ // virtual void* mutable_user_data() { return &my_data_; }
+ // private:
+ // MyData my_data_;
+ // };
+ // class MyPolygon : public S2Polygon::Shape {
+ // public:
+ // virtual void* mutable_user_data() { return &my_data_; }
+ // private:
+ // MyData my_data_;
+ // };
+ // ...
+ // S2Shape* shape = index.shape(id);
+ // const MyData* data = static_cast<const MyData*>(shape->user_data());
+ //
+ // This is not the only way to map from an S2Shape back to your source
+ // data. Other reasonable techniques include:
+ //
+ // - Every shape has an id() assigned by S2ShapeIndex. Ids are assigned
+ // sequentially starting from 0 in the order the shapes are added to the
+ // index. You can use this id to look up arbitrary data stored in your
+ // own vector.
+ //
+ // - If all of your shapes are the same type, then you can create your own
+ // subclass of some existing S2Shape type (such as S2Polyline::Shape) and
+ // add your own methods and fields. You can access this data by
+ // downcasting the S2Shape pointers returned by S2ShapeIndex methods.
+ virtual const void* user_data() const { return nullptr; }
+ virtual void* mutable_user_data() { return nullptr; }
+
+ private:
+ // Next available type tag available for use within the S2 library: 6.
+
+ friend class EncodedS2ShapeIndex;
+ friend class MutableS2ShapeIndex;
+
+ int id_; // Assigned by S2ShapeIndex when the shape is added.
+
+ S2Shape(const S2Shape&) = delete;
+ void operator=(const S2Shape&) = delete;
+};
+
+#endif // S2_S2SHAPE_H_
--- /dev/null
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// S2ShapeIndex is an abstract base class for indexing polygonal geometry in
+// memory. The main documentation is with the class definition below.
+// (Some helper classes are defined first.)
+
+#ifndef S2_S2SHAPE_INDEX_H_
+#define S2_S2SHAPE_INDEX_H_
+
+#include <array>
+#include <atomic>
+#include <cstddef>
+#include <iterator>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+#include "s2/base/mutex.h"
+#include "s2/base/spinlock.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2pointutil.h"
+#include "s2/s2shape.h"
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/third_party/absl/base/thread_annotations.h"
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/util/gtl/compact_array.h"
+
+class R1Interval;
+class S2PaddedCell;
+
+// S2ClippedShape represents the part of a shape that intersects an S2Cell.
+// It consists of the set of edge ids that intersect that cell, and a boolean
+// indicating whether the center of the cell is inside the shape (for shapes
+// that have an interior).
+//
+// Note that the edges themselves are not clipped; we always use the original
+// edges for intersection tests so that the results will be the same as the
+// original shape.
+class S2ClippedShape {
+ public:
+ // The shape id of the clipped shape.
+ int shape_id() const;
+
+ // Returns true if the center of the S2CellId is inside the shape. Returns
+ // false for shapes that do not have an interior.
+ bool contains_center() const;
+
+ // The number of edges that intersect the S2CellId.
+ int num_edges() const;
+
+ // Returns the edge id of the given edge in this clipped shape. Edges are
+ // sorted in increasing order of edge id.
+ //
+ // REQUIRES: 0 <= i < num_edges()
+ int edge(int i) const;
+
+ // Returns true if the clipped shape contains the given edge id.
+ bool ContainsEdge(int id) const;
+
+ private:
+ // This class may be copied by value, but note that it does *not* own its
+ // underlying data. (It is owned by the containing S2ShapeIndexCell.)
+
+ friend class MutableS2ShapeIndex;
+ friend class S2ShapeIndexCell;
+ friend class S2Stats;
+
+ // Internal methods are documented with their definition.
+ void Init(int32 shape_id, int32 num_edges);
+ void Destruct();
+ bool is_inline() const;
+ void set_contains_center(bool contains_center);
+ void set_edge(int i, int edge);
+
+ // All fields are packed into 16 bytes (assuming 64-bit pointers). Up to
+ // two edge ids are stored inline; this is an important optimization for
+ // clients that use S2Shapes consisting of a single edge.
+ int32 shape_id_;
+ uint32 contains_center_ : 1; // shape contains the cell center
+ uint32 num_edges_ : 31;
+
+ // If there are more than two edges, this field holds a pointer.
+ // Otherwise it holds an array of edge ids.
+ union {
+ int32* edges_; // Owned by the containing S2ShapeIndexCell.
+ std::array<int32, 2> inline_edges_;
+ };
+};
+
+// S2ShapeIndexCell stores the index contents for a particular S2CellId.
+// It consists of a set of clipped shapes.
+class S2ShapeIndexCell {
+ public:
+ S2ShapeIndexCell() {}
+ ~S2ShapeIndexCell();
+
+ // Returns the number of clipped shapes in this cell.
+ int num_clipped() const { return shapes_.size(); }
+
+ // Returns the clipped shape at the given index. Shapes are kept sorted in
+ // increasing order of shape id.
+ //
+ // REQUIRES: 0 <= i < num_clipped()
+ const S2ClippedShape& clipped(int i) const { return shapes_[i]; }
+
+ // Returns a pointer to the clipped shape corresponding to the given shape,
+ // or nullptr if the shape does not intersect this cell.
+ const S2ClippedShape* find_clipped(const S2Shape* shape) const;
+ const S2ClippedShape* find_clipped(int shape_id) const;
+
+ // Convenience method that returns the total number of edges in all clipped
+ // shapes.
+ int num_edges() const;
+
+ // Appends an encoded representation of the S2ShapeIndexCell to "encoder".
+ // "num_shape_ids" should be set to index.num_shape_ids(); this information
+ // allows the encoding to be more compact in some cases.
+ //
+ // REQUIRES: "encoder" uses the default constructor, so that its buffer
+ // can be enlarged as necessary by calling Ensure(int).
+ void Encode(int num_shape_ids, Encoder* encoder) const;
+
+ // Decodes an S2ShapeIndexCell, returning true on success.
+ // "num_shape_ids" should be set to index.num_shape_ids().
+ bool Decode(int num_shape_ids, Decoder* decoder);
+
+ private:
+ friend class MutableS2ShapeIndex;
+ friend class EncodedS2ShapeIndex;
+ friend class S2Stats;
+
+ // Internal methods are documented with their definitions.
+ S2ClippedShape* add_shapes(int n);
+ static void EncodeEdges(const S2ClippedShape& clipped, Encoder* encoder);
+ static bool DecodeEdges(int num_edges, S2ClippedShape* clipped,
+ Decoder* decoder);
+
+ using S2ClippedShapeSet = gtl::compact_array<S2ClippedShape>;
+ S2ClippedShapeSet shapes_;
+
+ S2ShapeIndexCell(const S2ShapeIndexCell&) = delete;
+ void operator=(const S2ShapeIndexCell&) = delete;
+};
+
+// S2ShapeIndex is an abstract base class for indexing polygonal geometry in
+// memory. The objects in the index are known as "shapes", and may consist of
+// points, polylines, and/or polygons, possibly overlapping. The index makes
+// it very fast to answer queries such as finding nearby shapes, measuring
+// distances, testing for intersection and containment, etc.
+//
+// Each object in the index implements the S2Shape interface. An S2Shape is a
+// collection of edges that optionally defines an interior. The edges do not
+// need to be connected, so for example an S2Shape can represent a polygon
+// with multiple shells and/or holes, or a set of polylines, or a set of
+// points. All geometry within a single S2Shape must have the same dimension,
+// so for example if you want to create an S2ShapeIndex containing a polyline
+// and 10 points, then you will need at least two different S2Shape objects.
+//
+// The most important type of S2ShapeIndex is MutableS2ShapeIndex, which
+// allows you to build an index incrementally by adding or removing shapes.
+// Soon there will also be an EncodedS2ShapeIndex type that makes it possible
+// to keep the index data in encoded form. Code that only needs read-only
+// ("const") access to an index should use the S2ShapeIndex base class as the
+// parameter type, so that it will work with any S2ShapeIndex subtype. For
+// example:
+//
+// void DoSomething(const S2ShapeIndex& index) {
+// ... works with MutableS2ShapeIndex or EncodedS2ShapeIndex ...
+// }
+//
+// There are a number of built-in classes that work with S2ShapeIndex objects.
+// Generally these classes accept any collection of geometry that can be
+// represented by an S2ShapeIndex, i.e. any combination of points, polylines,
+// and polygons. Such classes include:
+//
+// - S2ContainsPointQuery: returns the shape(s) that contain a given point.
+//
+// - S2ClosestEdgeQuery: returns the closest edge(s) to a given point, edge,
+// S2CellId, or S2ShapeIndex.
+//
+// - S2CrossingEdgeQuery: returns the edge(s) that cross a given edge.
+//
+// - S2BooleanOperation: computes boolean operations such as union,
+// and boolean predicates such as containment.
+//
+// - S2ShapeIndexRegion: computes approximations for a collection of geometry.
+//
+// - S2ShapeIndexBufferedRegion: computes approximations that have been
+// expanded by a given radius.
+//
+// Here is an example showing how to index a set of polygons and then
+// determine which polygon(s) contain each of a set of query points:
+//
+// void TestContainment(const vector<S2Point>& points,
+// const vector<S2Polygon*>& polygons) {
+// MutableS2ShapeIndex index;
+// for (auto polygon : polygons) {
+// index.Add(absl::make_unique<S2Polygon::Shape>(polygon));
+// }
+// auto query = MakeS2ContainsPointQuery(&index);
+// for (const auto& point : points) {
+// for (S2Shape* shape : query.GetContainingShapes(point)) {
+// S2Polygon* polygon = polygons[shape->id()];
+// ... do something with (point, polygon) ...
+// }
+// }
+// }
+//
+// This example uses S2Polygon::Shape, which is one example of an S2Shape
+// object. S2Polyline and S2Loop also have nested Shape classes, and there are
+// additional S2Shape types defined in *_shape.h.
+//
+// Internally, an S2ShapeIndex is essentially a map from S2CellIds to the set
+// of shapes that intersect each S2CellId. It is adaptively refined to ensure
+// that no cell contains more than a small number of edges.
+//
+// In addition to implementing a shared set of virtual methods, all
+// S2ShapeIndex subtypes define an Iterator type with the same API. This
+// makes it easy to convert code that uses a particular S2ShapeIndex subtype
+// to instead use the abstract base class (or vice versa). You can also
+// choose to avoid the overhead of virtual method calls by making the
+// S2ShapeIndex type a template argument, like this:
+//
+// template <class IndexType>
+// void DoSomething(const IndexType& index) {
+// for (typename IndexType::Iterator it(&index, S2ShapeIndex::BEGIN);
+// !it.done(); it.Next()) {
+// ...
+// }
+// }
+//
+// Subtypes provided by the S2 library have the same thread-safety properties
+// as std::vector. That is, const methods may be called concurrently from
+// multiple threads, and non-const methods require exclusive access to the
+// S2ShapeIndex.
+class S2ShapeIndex {
+ protected:
+ class IteratorBase;
+
+ public:
+ virtual ~S2ShapeIndex() {}
+
+ // Returns the number of distinct shape ids in the index. This is the same
+ // as the number of shapes provided that no shapes have ever been removed.
+ // (Shape ids are never reused.)
+ virtual int num_shape_ids() const = 0;
+
+ // Returns a pointer to the shape with the given id, or nullptr if the shape
+ // has been removed from the index.
+ virtual S2Shape* shape(int id) const = 0;
+
+ // Allows iterating over the indexed shapes using range-based for loops:
+ //
+ // for (S2Shape* shape : index) { ... }
+ //
+ // CAVEAT: Returns nullptr for shapes that have been removed from the index.
+ class ShapeIterator
+ : public std::iterator<std::forward_iterator_tag, S2Shape*> {
+ public:
+ ShapeIterator() = default;
+ S2Shape* operator*() const;
+ ShapeIterator& operator++();
+ ShapeIterator operator++(int);
+
+ // REQUIRES: "it" and *this must reference the same S2ShapeIndex.
+ bool operator==(ShapeIterator it) const;
+
+ // REQUIRES: "it" and *this must reference the same S2ShapeIndex.
+ bool operator!=(ShapeIterator it) const;
+
+ private:
+ friend class S2ShapeIndex;
+ ShapeIterator(const S2ShapeIndex* index, int shape_id)
+ : index_(index), shape_id_(shape_id) {}
+
+ const S2ShapeIndex* index_ = nullptr;
+ int shape_id_ = 0;
+ };
+ ShapeIterator begin() const;
+ ShapeIterator end() const;
+
+ // Returns the number of bytes currently occupied by the index (including any
+ // unused space at the end of vectors, etc).
+ virtual size_t SpaceUsed() const = 0;
+
+ // Minimizes memory usage by requesting that any data structures that can be
+ // rebuilt should be discarded. This method invalidates all iterators.
+ //
+ // Like all non-const methods, this method is not thread-safe.
+ virtual void Minimize() = 0;
+
+ // The possible relationships between a "target" cell and the cells of the
+ // S2ShapeIndex. If the target is an index cell or is contained by an index
+ // cell, it is "INDEXED". If the target is subdivided into one or more
+ // index cells, it is "SUBDIVIDED". Otherwise it is "DISJOINT".
+ enum CellRelation {
+ INDEXED, // Target is contained by an index cell
+ SUBDIVIDED, // Target is subdivided into one or more index cells
+ DISJOINT // Target does not intersect any index cells
+ };
+
+ // When passed to an Iterator constructor, specifies whether the iterator
+ // should be positioned at the beginning of the index (BEGIN), the end of
+ // the index (END), or arbitrarily (UNPOSITIONED). By default iterators are
+ // unpositioned, since this avoids an extra seek in this situation where one
+ // of the seek methods (such as Locate) is immediately called.
+ enum InitialPosition { BEGIN, END, UNPOSITIONED };
+
+ // A random access iterator that provides low-level access to the cells of
+ // the index. Cells are sorted in increasing order of S2CellId.
+ class Iterator {
+ public:
+ // Default constructor; must be followed by a call to Init().
+ Iterator() : iter_(nullptr) {}
+
+ // Constructs an iterator positioned as specified. By default iterators
+ // are unpositioned, since this avoids an extra seek in this situation
+ // where one of the seek methods (such as Locate) is immediately called.
+ //
+ // If you want to position the iterator at the beginning, e.g. in order to
+ // loop through the entire index, do this instead:
+ //
+ // for (S2ShapeIndex::Iterator it(&index, S2ShapeIndex::BEGIN);
+ // !it.done(); it.Next()) { ... }
+ explicit Iterator(const S2ShapeIndex* index,
+ InitialPosition pos = UNPOSITIONED)
+ : iter_(index->NewIterator(pos)) {}
+
+ // Initializes an iterator for the given S2ShapeIndex. This method may
+ // also be called in order to restore an iterator to a valid state after
+ // the underlying index has been updated (although it is usually easier
+ // just to declare a new iterator whenever required, since iterator
+ // construction is cheap).
+ void Init(const S2ShapeIndex* index,
+ InitialPosition pos = UNPOSITIONED) {
+ iter_ = index->NewIterator(pos);
+ }
+
+ // Iterators are copyable and movable.
+ Iterator(const Iterator&);
+ Iterator& operator=(const Iterator&);
+ Iterator(Iterator&&);
+ Iterator& operator=(Iterator&&);
+
+ // Returns the S2CellId of the current index cell. If done() is true,
+ // returns a value larger than any valid S2CellId (S2CellId::Sentinel()).
+ S2CellId id() const { return iter_->id(); }
+
+ // Returns the center point of the cell.
+ // REQUIRES: !done()
+ S2Point center() const { return id().ToPoint(); }
+
+ // Returns a reference to the contents of the current index cell.
+ // REQUIRES: !done()
+ const S2ShapeIndexCell& cell() const { return iter_->cell(); }
+
+ // Returns true if the iterator is positioned past the last index cell.
+ bool done() const { return iter_->done(); }
+
+ // Positions the iterator at the first index cell (if any).
+ void Begin() { iter_->Begin(); }
+
+ // Positions the iterator past the last index cell.
+ void Finish() { iter_->Finish(); }
+
+ // Positions the iterator at the next index cell.
+ // REQUIRES: !done()
+ void Next() { iter_->Next(); }
+
+ // If the iterator is already positioned at the beginning, returns false.
+ // Otherwise positions the iterator at the previous entry and returns true.
+ bool Prev() { return iter_->Prev(); }
+
+ // Positions the iterator at the first cell with id() >= target, or at the
+ // end of the index if no such cell exists.
+ void Seek(S2CellId target) { iter_->Seek(target); }
+
+ // Positions the iterator at the cell containing "target". If no such cell
+ // exists, returns false and leaves the iterator positioned arbitrarily.
+ // The returned index cell is guaranteed to contain all edges that might
+ // intersect the line segment between "target" and the cell center.
+ bool Locate(const S2Point& target) {
+ return IteratorBase::LocateImpl(target, this);
+ }
+
+ // Let T be the target S2CellId. If T is contained by some index cell I
+ // (including equality), this method positions the iterator at I and
+ // returns INDEXED. Otherwise if T contains one or more (smaller) index
+ // cells, it positions the iterator at the first such cell I and returns
+ // SUBDIVIDED. Otherwise it returns DISJOINT and leaves the iterator
+ // positioned arbitrarily.
+ CellRelation Locate(S2CellId target) {
+ return IteratorBase::LocateImpl(target, this);
+ }
+
+ private:
+ // Although S2ShapeIndex::Iterator can be used to iterate over any
+ // index subtype, it is more efficient to use the subtype's iterator when
+ // the subtype is known at compile time. For example, MutableS2ShapeIndex
+ // should use a MutableS2ShapeIndex::Iterator.
+ //
+ // The following declarations prevent accidental use of
+ // S2ShapeIndex::Iterator when the actual subtype is known. (If you
+ // really want to do this, you can down_cast the index argument to
+ // S2ShapeIndex.)
+ template <class T>
+ explicit Iterator(const T* index, InitialPosition pos = UNPOSITIONED) {}
+
+ template <class T>
+ void Init(const T* index, InitialPosition pos = UNPOSITIONED) {}
+
+ std::unique_ptr<IteratorBase> iter_;
+ };
+
+ // ShapeFactory is an interface for decoding vectors of S2Shapes. It allows
+ // random access to the shapes in order to support lazy decoding. See
+ // s2shapeutil_coding.h for useful subtypes.
+ class ShapeFactory {
+ public:
+ virtual ~ShapeFactory() {}
+
+ // Returns the number of S2Shapes in the vector.
+ virtual int size() const = 0;
+
+ // Returns the S2Shape object corresponding to the given "shape_id".
+ // Returns nullptr if a shape cannot be decoded or a shape is missing
+ // (e.g., because MutableS2ShapeIndex::Release() was called).
+ virtual std::unique_ptr<S2Shape> operator[](int shape_id) const = 0;
+
+ // Returns a deep copy of this ShapeFactory.
+ virtual std::unique_ptr<ShapeFactory> Clone() const = 0;
+ };
+
+ protected:
+ // Each subtype of S2ShapeIndex should define an Iterator type derived
+ // from the following base class.
+ class IteratorBase {
+ public:
+ virtual ~IteratorBase() {}
+
+ IteratorBase(const IteratorBase&);
+ IteratorBase& operator=(const IteratorBase&);
+
+ // Returns the S2CellId of the current index cell. If done() is true,
+ // returns a value larger than any valid S2CellId (S2CellId::Sentinel()).
+ S2CellId id() const;
+
+ // Returns the center point of the cell.
+ // REQUIRES: !done()
+ S2Point center() const;
+
+ // Returns a reference to the contents of the current index cell.
+ // REQUIRES: !done()
+ const S2ShapeIndexCell& cell() const;
+
+ // Returns true if the iterator is positioned past the last index cell.
+ bool done() const;
+
+ // Positions the iterator at the first index cell (if any).
+ virtual void Begin() = 0;
+
+ // Positions the iterator past the last index cell.
+ virtual void Finish() = 0;
+
+ // Positions the iterator at the next index cell.
+ // REQUIRES: !done()
+ virtual void Next() = 0;
+
+ // If the iterator is already positioned at the beginning, returns false.
+ // Otherwise positions the iterator at the previous entry and returns true.
+ virtual bool Prev() = 0;
+
+ // Positions the iterator at the first cell with id() >= target, or at the
+ // end of the index if no such cell exists.
+ virtual void Seek(S2CellId target) = 0;
+
+ // Positions the iterator at the cell containing "target". If no such cell
+ // exists, returns false and leaves the iterator positioned arbitrarily.
+ // The returned index cell is guaranteed to contain all edges that might
+ // intersect the line segment between "target" and the cell center.
+ virtual bool Locate(const S2Point& target) = 0;
+
+ // Let T be the target S2CellId. If T is contained by some index cell I
+ // (including equality), this method positions the iterator at I and
+ // returns INDEXED. Otherwise if T contains one or more (smaller) index
+ // cells, it positions the iterator at the first such cell I and returns
+ // SUBDIVIDED. Otherwise it returns DISJOINT and leaves the iterator
+ // positioned arbitrarily.
+ virtual CellRelation Locate(S2CellId target) = 0;
+
+ protected:
+ IteratorBase() : id_(S2CellId::Sentinel()), cell_(nullptr) {}
+
+ // Sets the iterator state. "cell" typically points to the cell contents,
+ // but may also be given as "nullptr" in order to implement decoding on
+ // demand. In that situation, the first that the client attempts to
+ // access the cell contents, the GetCell() method is called and "cell_" is
+ // updated in a thread-safe way.
+ void set_state(S2CellId id, const S2ShapeIndexCell* cell);
+
+ // Sets the iterator state so that done() is true.
+ void set_finished();
+
+ // Returns the current contents of the "cell_" field, which may be nullptr
+ // if the cell contents have not been decoded yet.
+ const S2ShapeIndexCell* raw_cell() const;
+
+ // This method is called to decode the contents of the current cell, if
+ // set_state() was previously called with a nullptr "cell" argument. This
+ // allows decoding on demand for subtypes that keep the cell contents in
+ // an encoded state. It does not need to be implemented at all if
+ // set_state() is always called with (cell != nullptr).
+ //
+ // REQUIRES: This method is thread-safe.
+ // REQUIRES: Multiple calls to this method return the same value.
+ virtual const S2ShapeIndexCell* GetCell() const = 0;
+
+ // Returns an exact copy of this iterator.
+ virtual std::unique_ptr<IteratorBase> Clone() const = 0;
+
+ // Makes a copy of the given source iterator.
+ // REQUIRES: "other" has the same concrete type as "this".
+ virtual void Copy(const IteratorBase& other) = 0;
+
+ // The default implementation of Locate(S2Point). It is instantiated by
+ // each subtype in order to (1) minimize the number of virtual method
+ // calls (since subtypes are typically "final") and (2) ensure that the
+ // correct versions of non-virtual methods such as cell() are called.
+ template <class Iter>
+ static bool LocateImpl(const S2Point& target, Iter* it);
+
+ // The default implementation of Locate(S2CellId) (see comments above).
+ template <class Iter>
+ static CellRelation LocateImpl(S2CellId target, Iter* it);
+
+ private:
+ friend class Iterator;
+
+ // This method is "const" because it is used internally by "const" methods
+ // in order to implement decoding on demand.
+ void set_cell(const S2ShapeIndexCell* cell) const;
+
+ S2CellId id_;
+ mutable std::atomic<const S2ShapeIndexCell*> cell_;
+ };
+
+ // Returns a new iterator positioned as specified.
+ virtual std::unique_ptr<IteratorBase> NewIterator(InitialPosition pos)
+ const = 0;
+};
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline int S2ClippedShape::shape_id() const {
+ return shape_id_;
+}
+
+inline bool S2ClippedShape::contains_center() const {
+ return contains_center_;
+}
+
+inline int S2ClippedShape::num_edges() const {
+ return num_edges_;
+}
+
+inline int S2ClippedShape::edge(int i) const {
+ return is_inline() ? inline_edges_[i] : edges_[i];
+}
+
+// Initialize an S2ClippedShape to hold the given number of edges.
+inline void S2ClippedShape::Init(int32 shape_id, int32 num_edges) {
+ shape_id_ = shape_id;
+ num_edges_ = num_edges;
+ contains_center_ = false;
+ if (!is_inline()) {
+ edges_ = new int32[num_edges];
+ }
+}
+
+// Free any memory allocated by this S2ClippedShape. We don't do this in
+// the destructor because S2ClippedShapes are copied by STL code, and we
+// don't want to repeatedly copy and free the edge data. Instead the data
+// is owned by the containing S2ShapeIndexCell.
+inline void S2ClippedShape::Destruct() {
+ if (!is_inline()) delete[] edges_;
+}
+
+inline bool S2ClippedShape::is_inline() const {
+ return num_edges_ <= inline_edges_.size();
+}
+
+// Set "contains_center_" to indicate whether this clipped shape contains the
+// center of the cell to which it belongs.
+inline void S2ClippedShape::set_contains_center(bool contains_center) {
+ contains_center_ = contains_center;
+}
+
+// Set the i-th edge of this clipped shape to be the given edge of the
+// original shape.
+inline void S2ClippedShape::set_edge(int i, int edge) {
+ if (is_inline()) {
+ inline_edges_[i] = edge;
+ } else {
+ edges_[i] = edge;
+ }
+}
+
+inline const S2ClippedShape* S2ShapeIndexCell::find_clipped(
+ const S2Shape* shape) const {
+ return find_clipped(shape->id());
+}
+
+// Inline because an index cell frequently contains just one shape.
+inline int S2ShapeIndexCell::num_edges() const {
+ int n = 0;
+ for (int i = 0; i < num_clipped(); ++i) n += clipped(i).num_edges();
+ return n;
+}
+
+inline S2Shape* S2ShapeIndex::ShapeIterator::operator*() const {
+ return index_->shape(shape_id_);
+}
+
+inline S2ShapeIndex::ShapeIterator& S2ShapeIndex::ShapeIterator::operator++() {
+ ++shape_id_;
+ return *this;
+}
+
+inline S2ShapeIndex::ShapeIterator S2ShapeIndex::ShapeIterator::operator++(
+ int) {
+ return ShapeIterator(index_, shape_id_++);
+}
+
+inline bool S2ShapeIndex::ShapeIterator::operator==(ShapeIterator it) const {
+ S2_DCHECK_EQ(index_, it.index_);
+ return shape_id_ == it.shape_id_;
+}
+
+inline bool S2ShapeIndex::ShapeIterator::operator!=(ShapeIterator it) const {
+ S2_DCHECK_EQ(index_, it.index_);
+ return shape_id_ != it.shape_id_;
+}
+
+inline S2ShapeIndex::ShapeIterator S2ShapeIndex::begin() const {
+ return ShapeIterator(this, 0);
+}
+
+inline S2ShapeIndex::ShapeIterator S2ShapeIndex::end() const {
+ return ShapeIterator(this, num_shape_ids());
+}
+
+inline S2ShapeIndex::IteratorBase::IteratorBase(const IteratorBase& other)
+ : id_(other.id_), cell_(other.raw_cell()) {
+}
+
+inline S2ShapeIndex::IteratorBase&
+S2ShapeIndex::IteratorBase::operator=(const IteratorBase& other) {
+ id_ = other.id_;
+ set_cell(other.raw_cell());
+ return *this;
+}
+
+inline S2CellId S2ShapeIndex::IteratorBase::id() const {
+ return id_;
+}
+
+inline const S2ShapeIndexCell& S2ShapeIndex::IteratorBase::cell() const {
+ // Like other const methods, this method is thread-safe provided that it
+ // does not overlap with calls to non-const methods.
+ S2_DCHECK(!done());
+ auto cell = raw_cell();
+ if (cell == nullptr) {
+ cell = GetCell();
+ set_cell(cell);
+ }
+ return *cell;
+}
+
+inline bool S2ShapeIndex::IteratorBase::done() const {
+ return id_ == S2CellId::Sentinel();
+}
+
+inline S2Point S2ShapeIndex::IteratorBase::center() const {
+ S2_DCHECK(!done());
+ return id().ToPoint();
+}
+
+inline void S2ShapeIndex::IteratorBase::set_state(
+ S2CellId id, const S2ShapeIndexCell* cell) {
+ id_ = id;
+ set_cell(cell);
+}
+
+inline void S2ShapeIndex::IteratorBase::set_finished() {
+ id_ = S2CellId::Sentinel();
+ set_cell(nullptr);
+}
+
+inline const S2ShapeIndexCell* S2ShapeIndex::IteratorBase::raw_cell()
+ const {
+ return cell_.load(std::memory_order_relaxed);
+}
+
+inline void S2ShapeIndex::IteratorBase::set_cell(
+ const S2ShapeIndexCell* cell) const {
+ cell_.store(cell, std::memory_order_relaxed);
+}
+
+template <class Iter>
+inline bool S2ShapeIndex::IteratorBase::LocateImpl(
+ const S2Point& target_point, Iter* it) {
+ // Let I = cell_map_->lower_bound(T), where T is the leaf cell containing
+ // "target_point". Then if T is contained by an index cell, then the
+ // containing cell is either I or I'. We test for containment by comparing
+ // the ranges of leaf cells spanned by T, I, and I'.
+
+ S2CellId target(target_point);
+ it->Seek(target);
+ if (!it->done() && it->id().range_min() <= target) return true;
+ if (it->Prev() && it->id().range_max() >= target) return true;
+ return false;
+}
+
+template <class Iter>
+inline S2ShapeIndex::CellRelation
+S2ShapeIndex::IteratorBase::LocateImpl(S2CellId target, Iter* it) {
+ // Let T be the target, let I = cell_map_->lower_bound(T.range_min()), and
+ // let I' be the predecessor of I. If T contains any index cells, then T
+ // contains I. Similarly, if T is contained by an index cell, then the
+ // containing cell is either I or I'. We test for containment by comparing
+ // the ranges of leaf cells spanned by T, I, and I'.
+
+ it->Seek(target.range_min());
+ if (!it->done()) {
+ if (it->id() >= target && it->id().range_min() <= target) return INDEXED;
+ if (it->id() <= target.range_max()) return SUBDIVIDED;
+ }
+ if (it->Prev() && it->id().range_max() >= target) return INDEXED;
+ return DISJOINT;
+}
+
+inline S2ShapeIndex::Iterator::Iterator(const Iterator& other)
+ : iter_(other.iter_->Clone()) {
+}
+
+inline S2ShapeIndex::Iterator& S2ShapeIndex::Iterator::operator=(
+ const Iterator& other) {
+ iter_->Copy(*other.iter_);
+ return *this;
+}
+
+inline S2ShapeIndex::Iterator::Iterator(Iterator&& other)
+ : iter_(std::move(other.iter_)) {
+}
+
+inline S2ShapeIndex::Iterator& S2ShapeIndex::Iterator::operator=(
+ Iterator&& other) {
+ iter_ = std::move(other.iter_);
+ return *this;
+}
+
+#endif // S2_S2SHAPE_INDEX_H_
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2SHAPE_INDEX_BUFFERED_REGION_H_
+#define S2_S2SHAPE_INDEX_BUFFERED_REGION_H_
+
+#include <vector>
+#include "s2/s2cap.h"
+#include "s2/s2cell.h"
+#include "s2/s2cell_union.h"
+#include "s2/s2closest_edge_query.h"
+#include "s2/s2latlng_rect.h"
+#include "s2/s2region.h"
+#include "s2/s2shape_index.h"
+
+// This class provides a way to expand an arbitrary collection of geometry by
+// a fixed radius (an operation variously known as "buffering", "offsetting",
+// or "Minkowski sum with a disc") in order to compute an S2CellId covering
+// (see S2RegionCoverer). The resulting covering contains all points within
+// the given radius of any point in the original geometry.
+//
+// This class does not actually buffer the geometry; instead it implements the
+// S2Region API by computing the distance from candidate S2CellIds to the
+// original geometry. If this distance is below the given radius then the
+// S2CellId intersects the buffered geometry. For example, if the original
+// geometry consists of a single S2Point then the buffered geometry is exactly
+// equivalent to an S2Cap with the given radius. (Note that the region is not
+// approximated as a polygonal loop.)
+//
+// Example usage:
+//
+// S2CellUnion GetBufferedCovering(const S2ShapeIndex& index, S1Angle radius) {
+// S2RegionCoverer coverer;
+// coverer.mutable_options()->set_max_cells(20);
+// S2CellUnion covering;
+// S2ShapeIndexBufferedRegion region(&index, radius);
+// coverer.GetCovering(region, &covering);
+// return covering;
+// }
+//
+// This class is not thread-safe. To use it in parallel, each thread should
+// construct its own instance (this is not expensive).
+class S2ShapeIndexBufferedRegion final : public S2Region {
+ public:
+ // Default constructor; requires Init() to be called.
+ S2ShapeIndexBufferedRegion();
+
+ // Constructs a region representing all points within the given radius of
+ // any point in the given S2ShapeIndex.
+ S2ShapeIndexBufferedRegion(const S2ShapeIndex* index,
+ S1ChordAngle radius);
+
+ // Convenience constructor that accepts an S1Angle for the radius.
+ // REQUIRES: radius >= S1Angle::Zero()
+ S2ShapeIndexBufferedRegion(const S2ShapeIndex* index, S1Angle radius)
+ : S2ShapeIndexBufferedRegion(index, S1ChordAngle(radius)) {}
+
+ // Equivalent to the constructor above.
+ void Init(const S2ShapeIndex* index, S1ChordAngle radius);
+
+ const S2ShapeIndex& index() const;
+ S1ChordAngle radius() const;
+
+ ////////////////////////////////////////////////////////////////////////
+ // S2Region interface (see s2region.h for details):
+
+ // Clone() returns a *shallow* copy; it does not make a copy of the
+ // underlying S2ShapeIndex.
+ S2ShapeIndexBufferedRegion* Clone() const override;
+
+ S2Cap GetCapBound() const override;
+ S2LatLngRect GetRectBound() const override;
+
+ // This method returns a small non-optimal covering that may include
+ // duplicate or overlapping cells. It should not be used directly.
+ // Instead, use S2RegionCoverer::GetCovering or GetFastCovering.
+ void GetCellUnionBound(std::vector<S2CellId> *cellids) const override;
+
+ // The implementation is approximate but conservative; it always returns
+ // "false" if the cell is not contained by the buffered region, but it may
+ // also return false in some cases where "cell" is in fact contained.
+ bool Contains(const S2Cell& cell) const override;
+
+ // Returns true if any buffered shape intersects "cell" (to within a very
+ // small error margin).
+ bool MayIntersect(const S2Cell& cell) const override;
+
+ // Returns true if the given point is contained by the buffered region,
+ // i.e. if it is within the given radius of any original shape.
+ bool Contains(const S2Point& p) const override;
+
+ private:
+ S1ChordAngle radius_;
+
+ // In order to handle (radius_ == 0) corectly, we need to test whether
+ // distances are less than or equal to "radius_". This is done by testing
+ // whether distances are less than radius_.Successor().
+ S1ChordAngle radius_successor_;
+
+ mutable S2ClosestEdgeQuery query_; // This class is not thread-safe!
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline S2ShapeIndexBufferedRegion::S2ShapeIndexBufferedRegion(
+ const S2ShapeIndex* index, S1ChordAngle radius)
+ : radius_(radius), radius_successor_(radius.Successor()), query_(index) {
+ query_.mutable_options()->set_include_interiors(true);
+}
+
+inline const S2ShapeIndex& S2ShapeIndexBufferedRegion::index() const {
+ return query_.index();
+}
+
+inline S1ChordAngle S2ShapeIndexBufferedRegion::radius() const {
+ return radius_;
+}
+
+#endif // S2_S2SHAPE_INDEX_BUFFERED_REGION_H_
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// Defines various angle and area measures for S2ShapeIndex objects. In
+// general, these methods return the sum of the corresponding measure for the
+// S2Shapes in the index.
+
+#ifndef S2_S2SHAPE_INDEX_MEASURES_H_
+#define S2_S2SHAPE_INDEX_MEASURES_H_
+
+#include "s2/s1angle.h"
+#include "s2/s2point.h"
+#include "s2/s2shape_index.h"
+
+namespace S2 {
+
+// Returns the maximum dimension of any shape in the index. Returns -1 if the
+// index does not contain any shapes.
+//
+// Note that the dimension does *not* depend on whether the shapes in the
+// index contain any points; for example, the dimension of an empty point set
+// is 0, and the dimension of an empty polygon is 2.
+int GetDimension(const S2ShapeIndex& index);
+
+// Returns the number of points (objects of dimension zero) in the index.
+// Note that polyline and polygon vertices are *not* included in this count.
+int GetNumPoints(const S2ShapeIndex& index);
+
+// Returns the total length of all polylines in the index. Returns zero if no
+// polylines are present.
+//
+// All edges are modeled as spherical geodesics. The result can be converted
+// to a distance on the Earth's surface (with a worst-case error of 0.562%
+// near the equator) using the functions in s2earth.h.
+S1Angle GetLength(const S2ShapeIndex& index);
+
+// Returns the total perimeter of all polygons in the index (including both
+// "shells" and "holes"). Returns zero if no polygons are present.
+//
+// All edges are modeled as spherical geodesics. The result can be converted
+// to a distance on the Earth's surface (with a worst-case error of 0.562%
+// near the equator) using the functions in s2earth.h.
+S1Angle GetPerimeter(const S2ShapeIndex& index);
+
+// Returns the total area of all polygons in the index. Returns zero if no
+// polygons are present. This method has good relative accuracy for both very
+// large and very small regions. Note that the result may exceed 4*Pi if the
+// index contains overlapping polygons.
+//
+// All edges are modeled as spherical geodesics. The result can be converted
+// to an area on the Earth's surface (with a worst-case error of 0.900% near
+// the poles) using the functions in s2earth.h.
+double GetArea(const S2ShapeIndex& index);
+
+// Like GetArea(), except that this method is faster and has more error. The
+// additional error is at most 2.22e-15 steradians per vertex, which works out
+// to about 0.09 square meters per vertex on the Earth's surface. For
+// example, a loop with 100 vertices has a maximum error of about 9 square
+// meters. (The actual error is typically much smaller than this.)
+double GetApproxArea(const S2ShapeIndex& index);
+
+// Returns the centroid of all shapes whose dimension is maximal within the
+// index, multiplied by the measure of those shapes. For example, if the
+// index contains points and polylines, then the result is defined as the
+// centroid of the polylines multiplied by the total length of those
+// polylines. The points would be ignored when computing the centroid.
+//
+// The measure of a given shape is defined as follows:
+//
+// - For dimension 0 shapes, the measure is shape->num_edges().
+// - For dimension 1 shapes, the measure is GetLength(shape).
+// - For dimension 2 shapes, the measure is GetArea(shape).
+//
+// Note that the centroid is not unit length, so you may need to call
+// Normalize() before passing it to other S2 functions. Note that this
+// function returns (0, 0, 0) if the index contains no geometry.
+//
+// The centroid is scaled by the total measure of the shapes for two reasons:
+// (1) it is cheaper to compute this way, and (2) this makes it easier to
+// compute the centroid of a collection of shapes (since the individual
+// centroids can simply be summed).
+S2Point GetCentroid(const S2ShapeIndex& index);
+
+} // namespace S2
+
+#endif // S2_S2SHAPE_INDEX_MEASURES_H_
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+#ifndef S2_S2SHAPE_INDEX_REGION_H_
+#define S2_S2SHAPE_INDEX_REGION_H_
+
+#include <vector>
+#include "s2/s2cap.h"
+#include "s2/s2cell.h"
+#include "s2/s2cell_union.h"
+#include "s2/s2contains_point_query.h"
+#include "s2/s2edge_clipping.h"
+#include "s2/s2edge_crosser.h"
+#include "s2/s2latlng_rect.h"
+#include "s2/s2region.h"
+#include "s2/s2shape_index.h"
+
+// This class wraps an S2ShapeIndex object with the additional methods needed
+// to implement the S2Region API, in order to allow S2RegionCoverer to compute
+// S2CellId coverings of arbitrary collections of geometry.
+//
+// These methods could conceivably be made part of S2ShapeIndex itself, but
+// there are several advantages to having a separate class:
+//
+// - The class can be templated in order to avoid virtual calls and memory
+// allocation (for iterators) when the concrete S2ShapeIndex type is known.
+//
+// - Implementing these methods efficiently requires an S2ShapeIndex iterator,
+// and this design allows a single iterator to be allocated and reused.
+//
+// - S2Region::Clone() is not a good fit for the S2ShapeIndex API because
+// it can't be implemented for some subtypes (e.g., EncodedS2ShapeIndex).
+//
+// Example usage:
+//
+// S2CellUnion GetCovering(const S2ShapeIndex& index) {
+// S2RegionCoverer coverer;
+// coverer.mutable_options()->set_max_cells(20);
+// S2CellUnion covering;
+// coverer.GetCovering(MakeS2ShapeIndexRegion(&index), &covering);
+// return covering;
+// }
+//
+// This class is not thread-safe. To use it in parallel, each thread should
+// construct its own instance (this is not expensive).
+template <class IndexType>
+class S2ShapeIndexRegion final : public S2Region {
+ public:
+ // Rather than calling this constructor, which requires specifying the
+ // S2ShapeIndex type explicitly, the preferred idiom is to call
+ // MakeS2ShapeIndexRegion(&index) instead. For example:
+ //
+ // coverer.GetCovering(MakeS2ShapeIndexRegion(&index), &covering);
+ explicit S2ShapeIndexRegion(const IndexType* index);
+
+ const IndexType& index() const;
+
+ ////////////////////////////////////////////////////////////////////////
+ // S2Region interface (see s2region.h for details):
+
+ // Clone() returns a *shallow* copy; it does not make a copy of the
+ // underlying S2ShapeIndex.
+ S2ShapeIndexRegion<IndexType>* Clone() const override;
+
+ S2Cap GetCapBound() const override;
+ S2LatLngRect GetRectBound() const override;
+
+ // This method currently returns at most 4 cells, unless the index spans
+ // multiple faces in which case it may return up to 6 cells.
+ void GetCellUnionBound(std::vector<S2CellId> *cell_ids) const override;
+
+ // Returns true if "target" is contained by any single shape. If the cell
+ // is covered by a union of different shapes then it may return false.
+ //
+ // The implementation is conservative but not exact; if a shape just barely
+ // contains the given cell then it may return false. The maximum error is
+ // less than 10 * DBL_EPSILON radians (or about 15 nanometers).
+ bool Contains(const S2Cell& target) const override;
+
+ // Returns true if any shape intersects "target".
+ //
+ // The implementation is conservative but not exact; if a shape is just
+ // barely disjoint from the given cell then it may return true. The maximum
+ // error is less than 10 * DBL_EPSILON radians (or about 15 nanometers).
+ bool MayIntersect(const S2Cell& target) const override;
+
+ // Returns true if the given point is contained by any two-dimensional shape
+ // (i.e., polygon). Boundaries are treated as being semi-open (i.e., the
+ // same rules as S2Polygon). Zero and one-dimensional shapes are ignored by
+ // this method (if you need more flexibility, see S2BooleanOperation).
+ bool Contains(const S2Point& p) const override;
+
+ private:
+ using Iterator = typename IndexType::Iterator;
+
+ static void CoverRange(S2CellId first, S2CellId last,
+ std::vector<S2CellId> *cell_ids);
+
+ // Returns true if the indexed shape "clipped" in the indexed cell "id"
+ // contains the point "p".
+ //
+ // REQUIRES: id.contains(S2CellId(p))
+ bool Contains(S2CellId id, const S2ClippedShape& clipped,
+ const S2Point& p) const;
+
+ // Returns true if any edge of the indexed shape "clipped" intersects the
+ // cell "target". It may also return true if an edge is very close to
+ // "target"; the maximum error is less than 10 * DBL_EPSILON radians (about
+ // 15 nanometers).
+ bool AnyEdgeIntersects(const S2ClippedShape& clipped,
+ const S2Cell& target) const;
+
+ // This class is not thread-safe!
+ mutable S2ContainsPointQuery<IndexType> contains_query_;
+
+ // Optimization: rather than declaring our own iterator, instead we reuse
+ // the iterator declared by S2ContainsPointQuery. (This improves benchmark
+ // times significantly for classes that create a new S2ShapeIndexRegion
+ // object on every call to Contains/MayIntersect(S2Cell).
+ Iterator& iter_ = *contains_query_.mutable_iter();
+};
+
+// Returns an S2ShapeIndexRegion that wraps the given S2ShapeIndex. Note that
+// it is efficient to return S2ShapeIndexRegion objects by value.
+template <class IndexType>
+S2ShapeIndexRegion<IndexType> MakeS2ShapeIndexRegion(const IndexType* index);
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+template <class IndexType>
+S2ShapeIndexRegion<IndexType>::S2ShapeIndexRegion(const IndexType* index)
+ : contains_query_(index) {
+}
+
+template <class IndexType>
+inline const IndexType& S2ShapeIndexRegion<IndexType>::index() const {
+ return contains_query_.index();
+}
+
+template <class IndexType>
+S2ShapeIndexRegion<IndexType>* S2ShapeIndexRegion<IndexType>::Clone() const {
+ return new S2ShapeIndexRegion<IndexType>(&index());
+}
+
+template <class IndexType>
+S2Cap S2ShapeIndexRegion<IndexType>::GetCapBound() const {
+ std::vector<S2CellId> covering;
+ GetCellUnionBound(&covering);
+ return S2CellUnion(std::move(covering)).GetCapBound();
+}
+
+template <class IndexType>
+S2LatLngRect S2ShapeIndexRegion<IndexType>::GetRectBound() const {
+ std::vector<S2CellId> covering;
+ GetCellUnionBound(&covering);
+ return S2CellUnion(std::move(covering)).GetRectBound();
+}
+
+template <class IndexType>
+void S2ShapeIndexRegion<IndexType>::GetCellUnionBound(
+ std::vector<S2CellId> *cell_ids) const {
+ // We find the range of S2Cells spanned by the index and choose a level such
+ // that the entire index can be covered with just a few cells. There are
+ // two cases:
+ //
+ // - If the index intersects two or more faces, then for each intersected
+ // face we add one cell to the covering. Rather than adding the entire
+ // face, instead we add the smallest S2Cell that covers the S2ShapeIndex
+ // cells within that face.
+ //
+ // - If the index intersects only one face, then we first find the smallest
+ // cell S that contains the index cells (just like the case above).
+ // However rather than using the cell S itself, instead we repeat this
+ // process for each of its child cells. In other words, for each
+ // child cell C we add the smallest S2Cell C' that covers the index cells
+ // within C. This extra step is relatively cheap and produces much
+ // tighter coverings when the S2ShapeIndex consists of a small region
+ // near the center of a large S2Cell.
+ //
+ // The following code uses only a single Iterator object because creating an
+ // Iterator may be relatively expensive for some S2ShapeIndex types (e.g.,
+ // it may involve memory allocation).
+ cell_ids->clear();
+ cell_ids->reserve(6);
+
+ // Find the last S2CellId in the index.
+ iter_.Finish();
+ if (!iter_.Prev()) return; // Empty index.
+ const S2CellId last_index_id = iter_.id();
+ iter_.Begin();
+ if (iter_.id() != last_index_id) {
+ // The index has at least two cells. Choose an S2CellId level such that
+ // the entire index can be spanned with at most 6 cells (if the index
+ // spans multiple faces) or 4 cells (it the index spans a single face).
+ int level = iter_.id().GetCommonAncestorLevel(last_index_id) + 1;
+
+ // For each cell C at the chosen level, we compute the smallest S2Cell
+ // that covers the S2ShapeIndex cells within C.
+ const S2CellId last_id = last_index_id.parent(level);
+ for (auto id = iter_.id().parent(level); id != last_id; id = id.next()) {
+ // If the cell C does not contain any index cells, then skip it.
+ if (id.range_max() < iter_.id()) continue;
+
+ // Find the range of index cells contained by C and then shrink C so
+ // that it just covers those cells.
+ S2CellId first = iter_.id();
+ iter_.Seek(id.range_max().next());
+ iter_.Prev();
+ CoverRange(first, iter_.id(), cell_ids);
+ iter_.Next();
+ }
+ }
+ CoverRange(iter_.id(), last_index_id, cell_ids);
+}
+
+// Computes the smallest S2Cell that covers the S2Cell range (first, last) and
+// adds this cell to "cell_ids".
+//
+// REQUIRES: "first" and "last" have a common ancestor.
+template <class IndexType>
+inline void S2ShapeIndexRegion<IndexType>::CoverRange(
+ S2CellId first, S2CellId last, std::vector<S2CellId> *cell_ids) {
+ if (first == last) {
+ // The range consists of a single index cell.
+ cell_ids->push_back(first);
+ } else {
+ // Add the lowest common ancestor of the given range.
+ int level = first.GetCommonAncestorLevel(last);
+ S2_DCHECK_GE(level, 0);
+ cell_ids->push_back(first.parent(level));
+ }
+}
+
+template <class IndexType>
+bool S2ShapeIndexRegion<IndexType>::Contains(const S2Cell& target) const {
+ S2ShapeIndex::CellRelation relation = iter_.Locate(target.id());
+
+ // If the relation is DISJOINT, then "target" is not contained. Similarly if
+ // the relation is SUBDIVIDED then "target" is not contained, since index
+ // cells are subdivided only if they (nearly) intersect too many edges.
+ if (relation != S2ShapeIndex::INDEXED) return false;
+
+ // Otherwise, the iterator points to an index cell containing "target".
+ // If any shape contains the target cell, we return true.
+ S2_DCHECK(iter_.id().contains(target.id()));
+ const S2ShapeIndexCell& cell = iter_.cell();
+ for (int s = 0; s < cell.num_clipped(); ++s) {
+ const S2ClippedShape& clipped = cell.clipped(s);
+ // The shape contains the target cell iff the shape contains the cell
+ // center and none of its edges intersects the (padded) cell interior.
+ if (iter_.id() == target.id()) {
+ if (clipped.num_edges() == 0 && clipped.contains_center()) return true;
+ } else {
+ // It is faster to call AnyEdgeIntersects() before Contains().
+ if (index().shape(clipped.shape_id())->dimension() == 2 &&
+ !AnyEdgeIntersects(clipped, target) &&
+ contains_query_.ShapeContains(iter_, clipped, target.GetCenter())) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+template <class IndexType>
+bool S2ShapeIndexRegion<IndexType>::MayIntersect(const S2Cell& target) const {
+ S2ShapeIndex::CellRelation relation = iter_.Locate(target.id());
+
+ // If "target" does not overlap any index cell, there is no intersection.
+ if (relation == S2ShapeIndex::DISJOINT) return false;
+
+ // If "target" is subdivided into one or more index cells, then there is an
+ // intersection to within the S2ShapeIndex error bound.
+ if (relation == S2ShapeIndex::SUBDIVIDED) return true;
+
+ // Otherwise, the iterator points to an index cell containing "target".
+ //
+ // If "target" is an index cell itself, there is an intersection because index
+ // cells are created only if they have at least one edge or they are
+ // entirely contained by the loop.
+ S2_DCHECK(iter_.id().contains(target.id()));
+ if (iter_.id() == target.id()) return true;
+
+ // Test whether any shape intersects the target cell or contains its center.
+ const S2ShapeIndexCell& cell = iter_.cell();
+ for (int s = 0; s < cell.num_clipped(); ++s) {
+ const S2ClippedShape& clipped = cell.clipped(s);
+ if (AnyEdgeIntersects(clipped, target)) return true;
+ if (contains_query_.ShapeContains(iter_, clipped, target.GetCenter())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+template <class IndexType>
+bool S2ShapeIndexRegion<IndexType>::Contains(const S2Point& p) const {
+ if (iter_.Locate(p)) {
+ const S2ShapeIndexCell& cell = iter_.cell();
+ for (int s = 0; s < cell.num_clipped(); ++s) {
+ if (contains_query_.ShapeContains(iter_, cell.clipped(s), p)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+template <class IndexType>
+bool S2ShapeIndexRegion<IndexType>::AnyEdgeIntersects(
+ const S2ClippedShape& clipped, const S2Cell& target) const {
+ static const double kMaxError = (S2::kFaceClipErrorUVCoord +
+ S2::kIntersectsRectErrorUVDist);
+ const R2Rect bound = target.GetBoundUV().Expanded(kMaxError);
+ const int face = target.face();
+ const S2Shape& shape = *index().shape(clipped.shape_id());
+ const int num_edges = clipped.num_edges();
+ for (int i = 0; i < num_edges; ++i) {
+ const auto edge = shape.edge(clipped.edge(i));
+ R2Point p0, p1;
+ if (S2::ClipToPaddedFace(edge.v0, edge.v1, face, kMaxError, &p0, &p1) &&
+ S2::IntersectsRect(p0, p1, bound)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+template <class IndexType>
+inline S2ShapeIndexRegion<IndexType> MakeS2ShapeIndexRegion(
+ const IndexType* index) {
+ return S2ShapeIndexRegion<IndexType>(index);
+}
+
+#endif // S2_S2SHAPE_INDEX_REGION_H_
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// Defines various angle and area measures for S2Shape objects. Unlike the
+// built-in S2Polygon and S2Polyline methods, these methods allow the
+// underlying data to be represented arbitrarily.
+
+#ifndef S2_S2SHAPE_MEASURES_H_
+#define S2_S2SHAPE_MEASURES_H_
+
+#include <vector>
+
+#include "s2/s1angle.h"
+#include "s2/s2point.h"
+#include "s2/s2shape.h"
+
+namespace S2 {
+
+// For shapes of dimension 1, returns the sum of all polyline lengths on the
+// unit sphere. Otherwise returns zero. (See GetPerimeter for shapes of
+// dimension 2.)
+//
+// All edges are modeled as spherical geodesics. The result can be converted
+// to a distance on the Earth's surface (with a worst-case error of 0.562%
+// near the equator) using the functions in s2earth.h.
+S1Angle GetLength(const S2Shape& shape);
+
+// For shapes of dimension 2, returns the sum of all loop perimeters on the
+// unit sphere. Otherwise returns zero. (See GetLength for shapes of
+// dimension 1.)
+//
+// All edges are modeled as spherical geodesics. The result can be converted
+// to a distance on the Earth's surface (with a worst-case error of 0.562%
+// near the equator) using the functions in s2earth.h.
+S1Angle GetPerimeter(const S2Shape& shape);
+
+// For shapes of dimension 2, returns the area of the shape on the unit
+// sphere. The result is between 0 and 4*Pi steradians. Otherwise returns
+// zero. This method has good relative accuracy for both very large and very
+// small regions.
+//
+// All edges are modeled as spherical geodesics. The result can be converted
+// to an area on the Earth's surface (with a worst-case error of 0.900% near
+// the poles) using the functions in s2earth.h.
+double GetArea(const S2Shape& shape);
+
+// Like GetArea(), except that this method is faster and has more error. The
+// additional error is at most 2.22e-15 steradians per vertex, which works out
+// to about 0.09 square meters per vertex on the Earth's surface. For
+// example, a loop with 100 vertices has a maximum error of about 9 square
+// meters. (The actual error is typically much smaller than this.)
+double GetApproxArea(const S2Shape& shape);
+
+// Returns the centroid of the shape multiplied by the measure of the shape,
+// which is defined as follows:
+//
+// - For dimension 0 shapes, the measure is shape->num_edges().
+// - For dimension 1 shapes, the measure is GetLength(shape).
+// - For dimension 2 shapes, the measure is GetArea(shape).
+//
+// Note that the result is not unit length, so you may need to call
+// Normalize() before passing it to other S2 functions.
+//
+// The result is scaled by the measure defined above for two reasons: (1) it
+// is cheaper to compute this way, and (2) this makes it easier to compute the
+// centroid of a collection of shapes. (This requires simply summing the
+// centroids of all shapes in the collection whose dimension is maximal.)
+S2Point GetCentroid(const S2Shape& shape);
+
+// Overwrites "vertices" with the vertices of the given edge chain of "shape".
+// If dimension == 1, the chain will have (chain.length + 1) vertices, and
+// otherwise it will have (chain.length) vertices.
+//
+// This is a low-level helper method used in the implementations of some of
+// the methods above.
+void GetChainVertices(const S2Shape& shape, int chain_id,
+ std::vector<S2Point>* vertices);
+
+} // namespace S2
+
+#endif // S2_S2SHAPE_MEASURES_H_
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2SHAPEUTIL_BUILD_POLYGON_BOUNDARIES_H_
+#define S2_S2SHAPEUTIL_BUILD_POLYGON_BOUNDARIES_H_
+
+#include <vector>
+
+#include "s2/s2shape_index.h"
+
+namespace s2shapeutil {
+
+// The purpose of this function is to construct polygons consisting of
+// multiple loops. It takes as input a collection of loops whose boundaries
+// do not cross, and groups them into polygons whose interiors do not
+// intersect (where the boundary of each polygon may consist of multiple
+// loops).
+//
+// some of those islands have lakes, then the input to this function would
+// islands, and their lakes. Each loop would actually be present twice, once
+// in each direction (see below). The output would consist of one polygon
+// representing each lake, one polygon representing each island not including
+// islands or their lakes, and one polygon representing the rest of the world
+//
+// This method is intended for internal use; external clients should use
+// S2Builder, which has more convenient interface.
+//
+// The input consists of a set of connected components, where each component
+// consists of one or more loops. The components must satisfy the following
+// properties:
+//
+// - The loops in each component must form a subdivision of the sphere (i.e.,
+// they must cover the entire sphere without overlap), except that a
+// component may consist of a single loop if and only if that loop is
+// degenerate (i.e., its interior is empty).
+//
+// - The boundaries of different components must be disjoint (i.e. no
+// crossing edges or shared vertices).
+//
+// - No component should be empty, and no loop should have zero edges.
+//
+// The output consists of a set of polygons, where each polygon is defined by
+// the collection of loops that form its boundary. This function does not
+// actually construct any S2Shapes; it simply identifies the loops that belong
+// to each polygon.
+void BuildPolygonBoundaries(
+ const std::vector<std::vector<S2Shape*>>& components,
+ std::vector<std::vector<S2Shape*>>* polygons);
+
+} // namespace s2shapeutil
+
+#endif // S2_S2SHAPEUTIL_BUILD_POLYGON_BOUNDARIES_H_
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// Helper functions for encoding/decoding S2Shapes and S2Shape vectors.
+// Design goals:
+//
+// - Allow control over encoding tradeoffs (i.e., speed vs encoding size).
+//
+// - Allow control over decoding tradeoffs (e.g., whether to decode data
+// immediately or lazily).
+//
+// - Don't force all S2Shape types to have the same Encode() method. Some
+// implementations may want extra parameters.
+//
+// - Support custom encodings of shape vectors; e.g., if all shapes are
+// of a known type, then there is no need to tag them individually.
+//
+// - Support client-defined S2Shape types.
+//
+// - Support client-defined encodings of standard S2Shape types.
+
+#ifndef S2_S2SHAPEUTIL_CODING_H_
+#define S2_S2SHAPEUTIL_CODING_H_
+
+#include <functional>
+#include <memory>
+#include "s2/util/coding/coder.h"
+#include "s2/encoded_string_vector.h"
+#include "s2/s2shape.h"
+#include "s2/s2shape_index.h"
+
+namespace s2shapeutil {
+
+// A function that appends a serialized representation of the given shape to
+// the given Encoder. The encoding should *not* include any type information
+// (e.g., shape->type_tag()); the caller is responsible for encoding this
+// separately if necessary.
+//
+// Note that you can add your own encodings and/or shape types by wrapping one
+// of the standard functions and adding exceptions:
+//
+// void MyShapeEncoder(const S2Shape& shape, Encoder* encoder) {
+// if (shape.type_tag() == MyShape::kTypeTag) {
+// down_cast<const MyShape*>(&shape)->Encode(encoder);
+// return true;
+// } else {
+// return CompactEncodeShape(shape, encoder);
+// }
+// }
+//
+// REQUIRES: "encoder" uses the default constructor, so that its buffer
+// can be enlarged as necessary by calling Ensure(int).
+using ShapeEncoder =
+ std::function<bool (const S2Shape& shape, Encoder* encoder)>;
+
+// A ShapeEncoder that can encode all standard S2Shape types, preferring fast
+// (but larger) encodings. For example, points are typically represented as
+// uncompressed S2Point values (24 bytes each).
+//
+// REQUIRES: "encoder" uses the default constructor, so that its buffer
+// can be enlarged as necessary by calling Ensure(int).
+bool FastEncodeShape(const S2Shape& shape, Encoder* encoder);
+
+// A ShapeEncoder that encode all standard S2Shape types, preferring
+// compact (but slower) encodings. For example, points that have been snapped
+// to S2CellId centers will be encoded using at most 8 bytes.
+//
+// REQUIRES: "encoder" uses the default constructor, so that its buffer
+// can be enlarged as necessary by calling Ensure(int).
+bool CompactEncodeShape(const S2Shape& shape, Encoder* encoder);
+
+// A function that decodes an S2Shape of the given type, consuming data from
+// the given Decoder. Returns nullptr on errors.
+using ShapeDecoder =
+ std::function<std::unique_ptr<S2Shape>(S2Shape::TypeTag tag,
+ Decoder* decoder)>;
+
+// A ShapeDecoder that fully decodes an S2Shape of the given type. After this
+// function returns, the underlying Decoder data is no longer needed.
+std::unique_ptr<S2Shape> FullDecodeShape(S2Shape::TypeTag tag,
+ Decoder* decoder);
+
+// A ShapeDecoder that prefers to decode the given S2Shape lazily (as data is
+// accessed). This is only possible when the given shape type (e.g.,
+// LaxPolygonShape) has an alternate implementation that can work directly
+// with encoded data (e.g., EncodedLaxPolygonShape). All other shape types
+// are handled by decoding them fully (e.g., S2Polygon::Shape).
+std::unique_ptr<S2Shape> LazyDecodeShape(S2Shape::TypeTag tag,
+ Decoder* decoder);
+
+// Encodes the shapes in the given S2ShapeIndex. Each shape is encoded with a
+// type tag allows it to be decoded into an S2Shape of the appropriate type.
+// "shape_encoder" allows control over the encoding strategy. Note that when
+// an S2ShapeIndex is also being encoded, it should be encoded *after* the
+// shape vector, like this:
+//
+// s2shapeutil::CompactEncodeTaggedShapes(index, encoder);
+// index.Encode(encoder);
+//
+// This is because when the index is decoded, the shape vector is required as
+// a parameter.
+//
+// REQUIRES: "encoder" uses the default constructor, so that its buffer
+// can be enlarged as necessary by calling Ensure(int).
+bool EncodeTaggedShapes(const S2ShapeIndex& index,
+ const ShapeEncoder& shape_encoder,
+ Encoder* encoder);
+
+// Convenience function that calls EncodeTaggedShapes using FastEncodeShape as
+// the ShapeEncoder.
+//
+// REQUIRES: "encoder" uses the default constructor, so that its buffer
+// can be enlarged as necessary by calling Ensure(int).
+bool FastEncodeTaggedShapes(const S2ShapeIndex& index, Encoder* encoder);
+
+// Convenience function that calls EncodeTaggedShapes using CompactEncodeShape
+// as the ShapeEncoder.
+//
+// REQUIRES: "encoder" uses the default constructor, so that its buffer
+// can be enlarged as necessary by calling Ensure(int).
+bool CompactEncodeTaggedShapes(const S2ShapeIndex& index, Encoder* encoder);
+
+// A ShapeFactory that decodes a vector generated by EncodeTaggedShapes()
+// above. Example usage:
+//
+// index.Init(decoder, s2shapeutil::FullDecodeShapeFactory(decoder));
+//
+// Note that the S2Shape vector must be encoded *before* the S2ShapeIndex
+// (like the example code for EncodeTaggedShapes), since the shapes need to be
+// decoded before the index.
+//
+// REQUIRES: The Decoder data buffer must outlive all calls to the given
+// ShapeFactory (not including its destructor).
+class TaggedShapeFactory : public S2ShapeIndex::ShapeFactory {
+ public:
+ // Returns an empty vector and/or null S2Shapes on decoding errors.
+ TaggedShapeFactory(const ShapeDecoder& shape_decoder, Decoder* decoder);
+
+ int size() const override { return encoded_shapes_.size(); }
+
+ std::unique_ptr<S2Shape> operator[](int shape_id) const override;
+
+ std::unique_ptr<ShapeFactory> Clone() const override {
+ return absl::make_unique<TaggedShapeFactory>(*this);
+ }
+
+ private:
+ ShapeDecoder shape_decoder_;
+ s2coding::EncodedStringVector encoded_shapes_;
+};
+
+// Convenience function that calls TaggedShapeFactory using FullDecodeShape
+// as the ShapeDecoder.
+TaggedShapeFactory FullDecodeShapeFactory(Decoder* decoder);
+
+// Convenience function that calls TaggedShapeFactory using LazyDecodeShape
+// as the ShapeDecoder.
+TaggedShapeFactory LazyDecodeShapeFactory(Decoder* decoder);
+
+// A ShapeFactory that simply returns shapes from the given vector.
+//
+// REQUIRES: Each shape is requested at most once. (This implies that when
+// the ShapeFactory is passed to an S2ShapeIndex, S2ShapeIndex::Minimize must
+// not be called.) Additional requests for the same shape return nullptr.
+class VectorShapeFactory : public S2ShapeIndex::ShapeFactory {
+ public:
+ explicit VectorShapeFactory(std::vector<std::unique_ptr<S2Shape>> shapes);
+
+ int size() const override { return shared_shapes_->size(); }
+
+ std::unique_ptr<S2Shape> operator[](int shape_id) const override;
+
+ std::unique_ptr<ShapeFactory> Clone() const override {
+ return absl::make_unique<VectorShapeFactory>(*this);
+ }
+
+ private:
+ // Since this class is copyable, we need to access the shape vector through
+ // a shared pointer.
+ std::shared_ptr<std::vector<std::unique_ptr<S2Shape>>> shared_shapes_;
+};
+
+// A ShapeFactory that returns the single given S2Shape. Useful for testing.
+VectorShapeFactory SingletonShapeFactory(std::unique_ptr<S2Shape> shape);
+
+// A ShapeFactory that wraps the shapes from the given index. Used for testing.
+class WrappedShapeFactory : public S2ShapeIndex::ShapeFactory {
+ public:
+ // REQUIRES: The given index must persist for the lifetime of this object.
+ explicit WrappedShapeFactory(const S2ShapeIndex* index) : index_(*index) {}
+
+ int size() const override { return index_.num_shape_ids(); }
+
+ std::unique_ptr<S2Shape> operator[](int shape_id) const override;
+
+ std::unique_ptr<ShapeFactory> Clone() const override {
+ return absl::make_unique<WrappedShapeFactory>(*this);
+ }
+
+ private:
+ const S2ShapeIndex& index_;
+};
+
+
+// Encodes the shapes in the given index, which must all have the same type.
+// The given Shape type does *not* need to have a "type tag" assigned.
+// This is useful for encoding experimental or locally defined types, or when
+// the S2Shape type in a given index is known in advance.
+//
+// REQUIRES: The Shape class must have an Encode(Encoder*) method.
+// REQUIRES: "encoder" uses the default constructor, so that its buffer
+// can be enlarged as necessary by calling Ensure(int).
+template <class Shape>
+void EncodeHomogeneousShapes(const S2ShapeIndex& index, Encoder* encoder);
+
+// A ShapeFactory that decodes shapes of a given fixed type (e.g.,
+// EncodedS2LaxPolylineShape). Example usage:
+//
+// REQUIRES: The Shape type must have an Init(Decoder*) method.
+// REQUIRES: The Decoder data buffer must outlive the returned ShapeFactory
+// and all shapes returned by that factory.
+template <class Shape>
+class HomogeneousShapeFactory : public S2ShapeIndex::ShapeFactory {
+ public:
+ // Returns an empty vector and/or null S2Shapes on decoding errors.
+ explicit HomogeneousShapeFactory(Decoder* decoder);
+
+ int size() const override { return encoded_shapes_.size(); }
+
+ std::unique_ptr<S2Shape> operator[](int shape_id) const override;
+
+ std::unique_ptr<ShapeFactory> Clone() const override {
+ return absl::make_unique<HomogeneousShapeFactory>(*this);
+ }
+
+ private:
+ s2coding::EncodedStringVector encoded_shapes_;
+};
+
+////////////////// Implementation details follow ////////////////////
+
+
+template <class Shape>
+void EncodeHomogeneousShapes(const S2ShapeIndex& index, Encoder* encoder) {
+ s2coding::StringVectorEncoder shape_vector;
+ for (S2Shape* shape : index) {
+ S2_DCHECK(shape != nullptr);
+ down_cast<Shape*>(shape)->Encode(shape_vector.AddViaEncoder());
+ }
+ shape_vector.Encode(encoder);
+}
+
+template <class Shape>
+HomogeneousShapeFactory<Shape>::HomogeneousShapeFactory(Decoder* decoder) {
+ if (!encoded_shapes_.Init(decoder)) encoded_shapes_.Clear();
+}
+
+template <class Shape>
+std::unique_ptr<S2Shape> HomogeneousShapeFactory<Shape>::operator[](
+ int shape_id) const {
+ Decoder decoder = encoded_shapes_.GetDecoder(shape_id);
+ auto shape = absl::make_unique<Shape>();
+ if (!shape->Init(&decoder)) return nullptr;
+ return std::move(shape); // Converts from Shape to S2Shape.
+}
+
+} // namespace s2shapeutil
+
+#endif // S2_S2SHAPEUTIL_CODING_H_
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2SHAPEUTIL_CONTAINS_BRUTE_FORCE_H_
+#define S2_S2SHAPEUTIL_CONTAINS_BRUTE_FORCE_H_
+
+#include "s2/s2point.h"
+#include "s2/s2shape_index.h"
+
+namespace s2shapeutil {
+
+// Returns true if the given shape contains the given point. Most clients
+// should not use this method, since its running time is linear in the number
+// of shape edges. Instead clients should create an S2ShapeIndex and use
+// S2ContainsPointQuery, since this strategy is much more efficient when many
+// points need to be tested.
+//
+// Polygon boundaries are treated as being semi-open (see S2ContainsPointQuery
+// and S2VertexModel for other options).
+//
+// CAVEAT: Typically this method is only used internally. Its running time is
+// linear in the number of shape edges.
+bool ContainsBruteForce(const S2Shape& shape, const S2Point& point);
+
+} // namespace s2shapeutil
+
+#endif // S2_S2SHAPEUTIL_CONTAINS_BRUTE_FORCE_H_
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2SHAPEUTIL_COUNT_EDGES_H_
+#define S2_S2SHAPEUTIL_COUNT_EDGES_H_
+
+#include "s2/s2shape_index.h"
+
+namespace s2shapeutil {
+
+// Returns the total number of edges in all indexed shapes. This method takes
+// time linear in the number of shapes.
+template <class S2ShapeIndexType>
+int CountEdges(const S2ShapeIndexType& index);
+
+// Like CountEdges(), but stops once "max_edges" edges have been found (in
+// which case the current running total is returned).
+template <class S2ShapeIndexType>
+int CountEdgesUpTo(const S2ShapeIndexType& index, int max_edges);
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+template <class S2ShapeIndexType>
+inline int CountEdges(const S2ShapeIndexType& index) {
+ return CountEdgesUpTo(index, std::numeric_limits<int>::max());
+}
+
+template <class S2ShapeIndexType>
+int CountEdgesUpTo(const S2ShapeIndexType& index, int max_edges) {
+ int num_edges = 0;
+ for (S2Shape* shape : index) {
+ if (shape == nullptr) continue;
+ num_edges += shape->num_edges();
+ if (num_edges >= max_edges) break;
+ }
+ return num_edges;
+}
+
+} // namespace s2shapeutil
+
+#endif // S2_S2SHAPEUTIL_COUNT_EDGES_H_
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef S2_S2SHAPEUTIL_EDGE_ITERATOR_H_
+#define S2_S2SHAPEUTIL_EDGE_ITERATOR_H_
+
+#include "s2/s2shape_index.h"
+#include "s2/s2shapeutil_shape_edge_id.h"
+
+namespace s2shapeutil {
+
+// An iterator that advances through all edges in an S2ShapeIndex.
+//
+// Example usage:
+//
+// for (EdgeIterator it(index); !it.Done(); it.Next()) {
+// auto edge = it.edge();
+// //...
+// }
+class EdgeIterator {
+ public:
+ explicit EdgeIterator(const S2ShapeIndex* index);
+
+ // Returns the current shape id.
+ int32 shape_id() const { return shape_id_; }
+
+ // Returns the current edge id.
+ int32 edge_id() const { return edge_id_; }
+
+ // Returns the current (shape_id, edge_id).
+ ShapeEdgeId shape_edge_id() const { return ShapeEdgeId(shape_id_, edge_id_); }
+
+ // Returns the current edge.
+ S2Shape::Edge edge() const;
+
+ // Returns true if there are no more edges in the index.
+ bool Done() const { return shape_id() >= index_->num_shape_ids(); }
+
+ // Advances to the next edge.
+ void Next();
+
+ bool operator==(const EdgeIterator& other) const {
+ return index_ == other.index_ && shape_id_ == other.shape_id_ &&
+ edge_id_ == other.edge_id_;
+ }
+
+ bool operator!=(const EdgeIterator& other) const { return !(*this == other); }
+
+ string DebugString() const;
+
+ private:
+ const S2ShapeIndex* index_;
+ int32 shape_id_;
+ int32 num_edges_;
+ int32 edge_id_;
+};
+
+} // namespace s2shapeutil
+
+#endif // S2_S2SHAPEUTIL_EDGE_ITERATOR_H_
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2SHAPEUTIL_GET_REFERENCE_POINT_H_
+#define S2_S2SHAPEUTIL_GET_REFERENCE_POINT_H_
+
+#include "s2/s2shape_index.h"
+
+namespace s2shapeutil {
+
+// This is a helper function for implementing S2Shape::GetReferencePoint().
+//
+// Given a shape consisting of closed polygonal loops, the interior of the
+// shape is defined as the region to the left of all edges (which must be
+// oriented consistently). This function then chooses an arbitrary point and
+// returns true if that point is contained by the shape.
+//
+// Unlike S2Loop and S2Polygon, this method allows duplicate vertices and
+// edges, which requires some extra care with definitions. The rule that we
+// apply is that an edge and its reverse edge "cancel" each other: the result
+// is the same as if that edge pair were not present. Therefore shapes that
+// consist only of degenerate loop(s) are either empty or full; by convention,
+// the shape is considered full if and only if it contains an empty loop (see
+// S2LaxPolygonShape for details).
+//
+// Determining whether a loop on the sphere contains a point is harder than
+// the corresponding problem in 2D plane geometry. It cannot be implemented
+// just by counting edge crossings because there is no such thing as a "point
+// at infinity" that is guaranteed to be outside the loop.
+S2Shape::ReferencePoint GetReferencePoint(const S2Shape& shape);
+
+} // namespace s2shapeutil
+
+#endif // S2_S2SHAPEUTIL_GET_REFERENCE_POINT_H_
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2SHAPEUTIL_RANGE_ITERATOR_H_
+#define S2_S2SHAPEUTIL_RANGE_ITERATOR_H_
+
+#include "s2/s2cell_id.h"
+#include "s2/s2shape_index.h"
+
+class S2Loop;
+class S2Error;
+
+namespace s2shapeutil {
+
+// RangeIterator is a wrapper over S2ShapeIndex::Iterator with extra methods
+// that are useful for merging the contents of two or more S2ShapeIndexes.
+class RangeIterator {
+ public:
+ // Construct a new RangeIterator positioned at the first cell of the index.
+ explicit RangeIterator(const S2ShapeIndex& index);
+
+ // The current S2CellId and cell contents.
+ S2CellId id() const { return it_.id(); }
+ const S2ShapeIndexCell& cell() const { return it_.cell(); }
+
+ // The min and max leaf cell ids covered by the current cell. If done() is
+ // true, these methods return a value larger than any valid cell id.
+ S2CellId range_min() const { return range_min_; }
+ S2CellId range_max() const { return range_max_; }
+
+ void Next();
+ bool done() { return it_.done(); }
+
+ // Position the iterator at the first cell that overlaps or follows
+ // "target", i.e. such that range_max() >= target.range_min().
+ void SeekTo(const RangeIterator& target);
+
+ // Position the iterator at the first cell that follows "target", i.e. the
+ // first cell such that range_min() > target.range_max().
+ void SeekBeyond(const RangeIterator& target);
+
+ private:
+ // Updates internal state after the iterator has been repositioned.
+ void Refresh();
+ S2ShapeIndex::Iterator it_;
+ S2CellId range_min_, range_max_;
+};
+
+} // namespace s2shapeutil
+
+#endif // S2_S2SHAPEUTIL_RANGE_ITERATOR_H_
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2SHAPEUTIL_SHAPE_EDGE_H_
+#define S2_S2SHAPEUTIL_SHAPE_EDGE_H_
+
+#include "s2/s2point.h"
+#include "s2/s2shape.h"
+#include "s2/s2shapeutil_shape_edge_id.h"
+
+namespace s2shapeutil {
+
+// A class representing a ShapeEdgeId together with the two endpoints of that
+// edge. It should be passed by reference.
+struct ShapeEdge {
+ public:
+ ShapeEdge() {}
+ ShapeEdge(const S2Shape& shape, int32 edge_id);
+ ShapeEdge(int32 shape_id, int32 edge_id, const S2Shape::Edge& edge);
+ ShapeEdgeId id() const { return id_; }
+ const S2Point& v0() const { return edge_.v0; }
+ const S2Point& v1() const { return edge_.v1; }
+
+ private:
+ ShapeEdgeId id_;
+ S2Shape::Edge edge_;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline ShapeEdge::ShapeEdge(const S2Shape& shape, int32 edge_id)
+ : ShapeEdge(shape.id(), edge_id, shape.edge(edge_id)) {
+}
+
+inline ShapeEdge::ShapeEdge(int32 shape_id, int32 edge_id,
+ const S2Shape::Edge& edge)
+ : id_(shape_id, edge_id), edge_(edge) {
+}
+
+} // namespace s2shapeutil
+
+#endif // S2_S2SHAPEUTIL_SHAPE_EDGE_H_
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2SHAPEUTIL_SHAPE_EDGE_ID_H_
+#define S2_S2SHAPEUTIL_SHAPE_EDGE_ID_H_
+
+#include <iostream>
+#include "s2/base/integral_types.h"
+
+namespace s2shapeutil {
+
+// ShapeEdgeId is a unique identifier for an edge within an S2ShapeIndex,
+// consisting of a (shape_id, edge_id) pair. It is similar to
+// std::pair<int32, int32> except that it has named fields.
+// It should be passed and returned by value.
+struct ShapeEdgeId {
+ public:
+ int32 shape_id, edge_id;
+ ShapeEdgeId() : shape_id(-1), edge_id(-1) {}
+ ShapeEdgeId(int32 _shape_id, int32 _edge_id);
+
+ bool operator==(ShapeEdgeId other) const;
+ bool operator!=(ShapeEdgeId other) const;
+ bool operator<(ShapeEdgeId other) const;
+ bool operator>(ShapeEdgeId other) const;
+ bool operator<=(ShapeEdgeId other) const;
+ bool operator>=(ShapeEdgeId other) const;
+};
+std::ostream& operator<<(std::ostream& os, ShapeEdgeId id);
+
+// Hasher for ShapeEdgeId.
+// Example use: std::unordered_set<ShapeEdgeId, ShapeEdgeIdHash>.
+struct ShapeEdgeIdHash;
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+inline ShapeEdgeId::ShapeEdgeId(int32 _shape_id, int32 _edge_id)
+ : shape_id(_shape_id), edge_id(_edge_id) {
+}
+
+inline bool ShapeEdgeId::operator==(ShapeEdgeId other) const {
+ return shape_id == other.shape_id && edge_id == other.edge_id;
+}
+
+inline bool ShapeEdgeId::operator!=(ShapeEdgeId other) const {
+ return !(*this == other);
+}
+
+inline bool ShapeEdgeId::operator<(ShapeEdgeId other) const {
+ if (shape_id < other.shape_id) return true;
+ if (other.shape_id < shape_id) return false;
+ return edge_id < other.edge_id;
+}
+
+inline bool ShapeEdgeId::operator>(ShapeEdgeId other) const {
+ return other < *this;
+}
+
+inline bool ShapeEdgeId::operator<=(ShapeEdgeId other) const {
+ return !(other < *this);
+}
+
+inline bool ShapeEdgeId::operator>=(ShapeEdgeId other) const {
+ return !(*this < other);
+}
+
+inline std::ostream& operator<<(std::ostream& os, ShapeEdgeId id) {
+ return os << id.shape_id << ":" << id.edge_id;
+}
+
+struct ShapeEdgeIdHash {
+ size_t operator()(ShapeEdgeId id) const {
+ // The following preserves all bits even when edge_id < 0.
+ return std::hash<uint64>()((static_cast<uint64>(id.shape_id) << 32) |
+ static_cast<uint32>(id.edge_id));
+ }
+};
+
+} // namespace s2shapeutil
+
+#endif // S2_S2SHAPEUTIL_SHAPE_EDGE_ID_H_
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2SHAPEUTIL_TESTING_H_
+#define S2_S2SHAPEUTIL_TESTING_H_
+
+#include "s2/s2shape.h"
+#include "s2/s2shape_index.h"
+
+namespace s2testing {
+
+// Verifies that all methods of the two S2Shapes return identical results,
+// except for id() and type_tag().
+void ExpectEqual(const S2Shape& a, const S2Shape& b);
+
+// Verifies that two S2ShapeIndexes have identical contents (including all the
+// S2Shapes in both indexes).
+void ExpectEqual(const S2ShapeIndex& a, const S2ShapeIndex& b);
+
+} // namespace s2testing
+
+#endif // S2_S2SHAPEUTIL_TESTING_H_
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2SHAPEUTIL_VISIT_CROSSING_EDGE_PAIRS_H_
+#define S2_S2SHAPEUTIL_VISIT_CROSSING_EDGE_PAIRS_H_
+
+#include <functional>
+#include "s2/s2crossing_edge_query.h"
+#include "s2/s2shape_index.h"
+#include "s2/s2shapeutil_shape_edge.h"
+
+class S2Error;
+
+namespace s2shapeutil {
+
+// A function that is called with pairs of crossing edges. The function may
+// return false in order to request that the algorithm should be terminated,
+// i.e. no further crossings are needed.
+//
+// "is_interior" indicates that the crossing is at a point interior to both
+// edges (i.e., not at a vertex). (The calling function already has this
+// information and it is moderately expensive to recompute.)
+using EdgePairVisitor = std::function<
+ bool (const ShapeEdge& a, const ShapeEdge& b, bool is_interior)>;
+
+// Visits all pairs of crossing edges in the given S2ShapeIndex, terminating
+// early if the given EdgePairVisitor function returns false (in which case
+// VisitCrossings returns false as well). "type" indicates whether all
+// crossings should be visited, or only interior crossings.
+//
+// CAVEAT: Crossings may be visited more than once.
+bool VisitCrossingEdgePairs(const S2ShapeIndex& index, CrossingType type,
+ const EdgePairVisitor& visitor);
+
+// Like the above, but visits all pairs of crossing edges where one edge comes
+// from each S2ShapeIndex.
+//
+// CAVEAT: Crossings may be visited more than once.
+bool VisitCrossingEdgePairs(const S2ShapeIndex& a_index,
+ const S2ShapeIndex& b_index,
+ CrossingType type, const EdgePairVisitor& visitor);
+
+// Given an S2ShapeIndex containing a single polygonal shape (e.g., an
+// S2Polygon or S2Loop), return true if any loop has a self-intersection
+// (including duplicate vertices) or crosses any other loop (including vertex
+// crossings and duplicate edges) and set "error" to a human-readable error
+// message. Otherwise return false and leave "error" unchanged.
+//
+// This method is used to implement the FindValidationError methods of S2Loop
+// and S2Polygon.
+//
+// TODO(ericv): Add an option to support S2LaxPolygonShape rules (i.e.,
+// duplicate vertices and edges are allowed, but loop crossings are not).
+bool FindSelfIntersection(const S2ShapeIndex& index, S2Error* error);
+
+} // namespace s2shapeutil
+
+#endif // S2_S2SHAPEUTIL_VISIT_CROSSING_EDGE_PAIRS_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2TESTING_H_
+#define S2_S2TESTING_H_
+#include "cpp-compat.h"
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "s2/base/commandlineflags.h"
+#include "s2/base/integral_types.h"
+#include "s2/_fp_contract_off.h"
+#include "s2/r2.h"
+#include "s2/s1angle.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s2cell_id.h"
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/util/math/matrix3x3.h"
+
+class S1Angle;
+class S2Cap;
+class S2CellUnion;
+class S2LatLng;
+class S2LatLngRect;
+class S2Loop;
+class S2Polygon;
+class S2Polyline;
+class S2Region;
+
+// You can optionally call S2Testing::rnd.Reset(FLAGS_s2_random_seed) at the
+// start of a test or benchmark to ensure that its results do not depend on
+// which other tests of benchmarks have run previously. This can help with
+// debugging.
+//
+// This flag currently does *not* affect the initial seed value for
+// S2Testing::rnd. TODO(user): Fix this.
+DECLARE_int32(s2_random_seed);
+
+// This class defines various static functions that are useful for writing
+// unit tests.
+class S2Testing {
+ public:
+ // Returns a vector of points shaped as a regular polygon with
+ // num_vertices vertices, all on a circle of the specified angular
+ // radius around the center. The radius is the actual distance from
+ // the center to the circle along the sphere.
+ //
+ // If you want to construct a regular polygon, try this:
+ // S2Polygon polygon(S2Loop::MakeRegularLoop(center, radius, num_vertices));
+ static std::vector<S2Point> MakeRegularPoints(const S2Point& center,
+ S1Angle radius,
+ int num_vertices);
+
+ // Append the vertices of "loop" to "vertices".
+ static void AppendLoopVertices(const S2Loop& loop,
+ std::vector<S2Point>* vertices);
+
+ // A simple class that generates "Koch snowflake" fractals (see Wikipedia
+ // for an introduction). There is an option to control the fractal
+ // dimension (between 1.0 and 2.0); values between 1.02 and 1.50 are
+ // reasonable simulations of various coastlines. The default dimension
+ // (about 1.26) corresponds to the standard Koch snowflake. (The west coast
+ // of Britain has a fractal dimension of approximately 1.25.)
+ //
+ // The fractal is obtained by starting with an equilateral triangle and
+ // recursively subdividing each edge into four segments of equal length.
+ // Therefore the shape at level "n" consists of 3*(4**n) edges. Multi-level
+ // fractals are also supported: if you set min_level() to a non-negative
+ // value, then the recursive subdivision has an equal probability of
+ // stopping at any of the levels between the given min and max (inclusive).
+ // This yields a fractal where the perimeter of the original triangle is
+ // approximately equally divided between fractals at the various possible
+ // levels. If there are k distinct levels {min,..,max}, the expected number
+ // of edges at each level "i" is approximately 3*(4**i)/k.
+ class Fractal {
+ public:
+ // You must call set_max_level() or SetLevelForApproxMaxEdges() before
+ // calling MakeLoop().
+ Fractal();
+
+ // Set the maximum subdivision level for the fractal (see above).
+ // REQUIRES: max_level >= 0
+ void set_max_level(int max_level);
+ int max_level() const { return max_level_; }
+
+ // Set the minimum subdivision level for the fractal (see above). The
+ // default value of -1 causes the min and max levels to be the same. A
+ // min_level of 0 should be avoided since this creates a significant
+ // chance that none of the three original edges will be subdivided at all.
+ //
+ // DEFAULT: max_level()
+ void set_min_level(int min_level_arg);
+ int min_level() const { return min_level_arg_; }
+
+ // Set the min and/or max level to produce approximately the given number
+ // of edges. (The values are rounded to a nearby value of 3*(4**n).)
+ void SetLevelForApproxMinEdges(int min_edges);
+ void SetLevelForApproxMaxEdges(int max_edges);
+
+ // Set the fractal dimension. The default value of approximately 1.26
+ // corresponds to the stardard Koch curve. The value must lie in the
+ // range [1.0, 2.0).
+ //
+ // DEFAULT: log(4) / log(3) ~= 1.26
+ void set_fractal_dimension(double dimension);
+ double fractal_dimension() const { return dimension_; }
+
+ // Return a lower bound on ratio (Rmin / R), where "R" is the radius
+ // passed to MakeLoop() and "Rmin" is the minimum distance from the
+ // fractal boundary to its center, where all distances are measured in the
+ // tangent plane at the fractal's center. This can be used to inscribe
+ // another geometric figure within the fractal without intersection.
+ double min_radius_factor() const;
+
+ // Return the ratio (Rmax / R), where "R" is the radius passed to
+ // MakeLoop() and "Rmax" is the maximum distance from the fractal boundary
+ // to its center, where all distances are measured in the tangent plane at
+ // the fractal's center. This can be used to inscribe the fractal within
+ // some other geometric figure without intersection.
+ double max_radius_factor() const;
+
+ // Return a fractal loop centered around the z-axis of the given
+ // coordinate frame, with the first vertex in the direction of the
+ // positive x-axis. In order to avoid self-intersections, the fractal is
+ // generated by first drawing it in a 2D tangent plane to the unit sphere
+ // (touching at the fractal's center point) and then projecting the edges
+ // onto the sphere. This has the side effect of shrinking the fractal
+ // slightly compared to its nominal radius.
+ std::unique_ptr<S2Loop> MakeLoop(const Matrix3x3_d& frame,
+ S1Angle nominal_radius) const;
+
+ private:
+ void ComputeMinLevel();
+ void ComputeOffsets();
+ void GetR2Vertices(std::vector<R2Point>* vertices) const;
+ void GetR2VerticesHelper(const R2Point& v0, const R2Point& v4, int level,
+ std::vector<R2Point>* vertices) const;
+
+ int max_level_;
+ int min_level_arg_; // Value set by user
+ int min_level_; // Actual min level (depends on max_level_)
+ double dimension_;
+
+ // The ratio of the sub-edge length to the original edge length at each
+ // subdivision step.
+ double edge_fraction_;
+
+ // The distance from the original edge to the middle vertex at each
+ // subdivision step, as a fraction of the original edge length.
+ double offset_fraction_;
+
+ Fractal(const Fractal&) = delete;
+ void operator=(const Fractal&) = delete;
+ };
+
+ // Convert a distance on the Earth's surface to an angle.
+ // Do not use these methods in non-testing code; use s2earth.h instead.
+ static S1Angle MetersToAngle(double meters);
+ static S1Angle KmToAngle(double km);
+
+ // Convert an area in steradians (as returned by the S2 area methods) to
+ // square meters or square kilometers.
+ static double AreaToMeters2(double steradians);
+ static double AreaToKm2(double steradians);
+
+ // The Earth's mean radius in kilometers (according to NASA).
+ static const double kEarthRadiusKm;
+
+ // A deterministically-seeded random number generator.
+ class Random;
+
+ static Random rnd;
+
+ // Return a random unit-length vector.
+ static S2Point RandomPoint();
+
+ // Return a right-handed coordinate frame (three orthonormal vectors).
+ static void GetRandomFrame(S2Point* x, S2Point* y, S2Point* z);
+ static Matrix3x3_d GetRandomFrame();
+
+ // Given a unit-length z-axis, compute x- and y-axes such that (x,y,z) is a
+ // right-handed coordinate frame (three orthonormal vectors).
+ static void GetRandomFrameAt(const S2Point& z, S2Point* x, S2Point *y);
+ static Matrix3x3_d GetRandomFrameAt(const S2Point& z);
+
+ // Return a cap with a random axis such that the log of its area is
+ // uniformly distributed between the logs of the two given values.
+ // (The log of the cap angle is also approximately uniformly distributed.)
+ static S2Cap GetRandomCap(double min_area, double max_area);
+
+ // Return a point chosen uniformly at random (with respect to area)
+ // from the given cap.
+ static S2Point SamplePoint(const S2Cap& cap);
+
+ // Return a point chosen uniformly at random (with respect to area on the
+ // sphere) from the given latitude-longitude rectangle.
+ static S2Point SamplePoint(const S2LatLngRect& rect);
+
+ // Return a random cell id at the given level or at a randomly chosen
+ // level. The distribution is uniform over the space of cell ids,
+ // but only approximately uniform over the surface of the sphere.
+ static S2CellId GetRandomCellId(int level);
+ static S2CellId GetRandomCellId();
+
+ // Return a polygon with the specified center, number of concentric loops
+ // and vertices per loop.
+ static void ConcentricLoopsPolygon(const S2Point& center,
+ int num_loops,
+ int num_vertices_per_loop,
+ S2Polygon* polygon);
+
+ // Checks that "covering" completely covers the given region. If
+ // "check_tight" is true, also checks that it does not contain any cells
+ // that do not intersect the given region. ("id" is only used internally.)
+ static void CheckCovering(const S2Region& region,
+ const S2CellUnion& covering,
+ bool check_tight,
+ S2CellId id = S2CellId());
+
+ private:
+ // Contains static methods
+ S2Testing() = delete;
+ S2Testing(const S2Testing&) = delete;
+ void operator=(const S2Testing&) = delete;
+};
+
+// Functions in this class return random numbers that are as good as random()
+// is. The results are reproducible since the seed is deterministic. This
+// class is *NOT* thread-safe; it is only intended for testing purposes.
+class S2Testing::Random {
+ public:
+ // Initialize using a deterministic seed.
+ Random();
+
+ // Reset the generator state using the given seed.
+ void Reset(int32 seed);
+
+ // Return a uniformly distributed 64-bit unsigned integer.
+ uint64 Rand64();
+
+ // Return a uniformly distributed 32-bit unsigned integer.
+ uint32 Rand32();
+
+ // Return a uniformly distributed "double" in the range [0,1). Note that
+ // the values returned are all multiples of 2**-53, which means that not all
+ // possible values in this range are returned.
+ double RandDouble();
+
+ // Return a uniformly distributed integer in the range [0,n).
+ int32 Uniform(int32 n);
+
+ // Return a uniformly distributed "double" in the range [min, limit).
+ double UniformDouble(double min, double limit);
+
+ // A functor-style version of Uniform, so that this class can be used with
+ // STL functions that require a RandomNumberGenerator concept.
+ int32 operator() (int32 n) {
+ return Uniform(n);
+ }
+
+ // Return true with probability 1 in n.
+ bool OneIn(int32 n);
+
+ // Skewed: pick "base" uniformly from range [0,max_log] and then
+ // return "base" random bits. The effect is to pick a number in the
+ // range [0,2^max_log-1] with bias towards smaller numbers.
+ int32 Skewed(int max_log);
+
+ private:
+ // Currently this class is based on random(), therefore it makes no sense to
+ // make a copy.
+ Random(const Random&) = delete;
+ void operator=(const Random&) = delete;
+};
+
+// Compare two sets of "closest" items, where "expected" is computed via brute
+// force (i.e., considering every possible candidate) and "actual" is computed
+// using a spatial data structure. Here "max_size" is a bound on the maximum
+// number of items, "max_distance" is a limit on the distance to any item, and
+// "max_error" is the maximum error allowed when selecting which items are
+// closest (see S2ClosestEdgeQuery::Options::max_error).
+template <typename Id, typename Distance>
+bool CheckDistanceResults(
+ const std::vector<std::pair<Distance, Id>>& expected,
+ const std::vector<std::pair<Distance, Id>>& actual,
+ int max_size, Distance max_distance, typename Distance::Delta max_error);
+
+
+//////////////////// Implementation Details Follow ////////////////////////
+
+
+namespace S2 {
+namespace internal {
+
+// Check that result set "x" contains all the expected results from "y", and
+// does not include any duplicate results.
+template <typename Id, typename Distance>
+bool CheckResultSet(const std::vector<std::pair<Distance, Id>>& x,
+ const std::vector<std::pair<Distance, Id>>& y,
+ int max_size, Distance max_distance,
+ typename Distance::Delta max_error,
+ typename Distance::Delta max_pruning_error,
+ const string& label) {
+ using Result = std::pair<Distance, Id>;
+ // Results should be sorted by distance, but not necessarily then by Id.
+ EXPECT_TRUE(std::is_sorted(x.begin(), x.end(),
+ [](const Result& x, const Result& y) {
+ return x.first < y.first;
+ }));
+
+ // Result set X should contain all the items from Y whose distance is less
+ // than "limit" computed below.
+ Distance limit = Distance::Zero();
+ if (x.size() < max_size) {
+ // Result set X was not limited by "max_size", so it should contain all
+ // the items up to "max_distance", except that a few items right near the
+ // distance limit may be missed because the distance measurements used for
+ // pruning S2Cells are not conservative.
+ if (max_distance == Distance::Infinity()) {
+ limit = max_distance;
+ } else {
+ limit = max_distance - max_pruning_error;
+ }
+ } else if (!x.empty()) {
+ // Result set X contains only the closest "max_size" items, to within a
+ // tolerance of "max_error + max_pruning_error".
+ limit = (x.back().first - max_error) - max_pruning_error;
+ }
+
+ bool result = true;
+ for (const auto& yp : y) {
+ // Note that this test also catches duplicate values.
+ int count = std::count_if(x.begin(), x.end(), [&yp](const Result& xp) {
+ return xp.second == yp.second;
+ });
+ if (yp.first < limit && count != 1) {
+ result = false;
+ cpp_compat_cout << (count > 1 ? "Duplicate" : label) << " distance = "
+ << S1ChordAngle(yp.first) << ", id = " << yp.second
+ << std::endl;
+ }
+ }
+
+ return result;
+}
+
+} // namespace internal
+} // namespace S2
+
+template <typename Id, typename Distance>
+bool CheckDistanceResults(
+ const std::vector<std::pair<Distance, Id>>& expected,
+ const std::vector<std::pair<Distance, Id>>& actual,
+ int max_size, Distance max_distance, typename Distance::Delta max_error) {
+ // This is a conservative bound on the error in computing the distance from
+ // the target geometry to an S2Cell. Such errors can cause candidates to be
+ // pruned from the result set even though they may be slightly closer.
+ static const typename Distance::Delta kMaxPruningError(
+ S1ChordAngle::Radians(1e-15));
+ return (S2::internal::CheckResultSet(
+ actual, expected, max_size, max_distance, max_error,
+ kMaxPruningError, "Missing") & /*not &&*/
+ S2::internal::CheckResultSet(
+ expected, actual, max_size, max_distance, max_error,
+ Distance::Delta::Zero(), "Extra"));
+}
+
+#endif // S2_S2TESTING_H_
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef S2_S2TEXT_FORMAT_H_
+#define S2_S2TEXT_FORMAT_H_
+
+// s2text_format contains a collection of functions for converting
+// geometry to and from a human-readable format. It is mainly
+// intended for testing and debugging. Be aware that the
+// human-readable format is *not* designed to preserve the full
+// precision of the original object, so it should not be used
+// for data storage.
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "s2/third_party/absl/base/attributes.h"
+#include "s2/third_party/absl/strings/string_view.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2cell_union.h"
+#include "s2/s2debug.h"
+#include "s2/s2latlng_rect.h"
+#include "s2/s2point_span.h"
+
+class MutableS2ShapeIndex;
+class S2LaxPolygonShape;
+class S2LaxPolylineShape;
+class S2Loop;
+class S2Polygon;
+class S2Polyline;
+class S2ShapeIndex;
+
+namespace s2textformat {
+
+// Returns an S2Point corresponding to the given a latitude-longitude
+// coordinate in degrees. Example of the input format:
+// "-20:150"
+S2Point MakePointOrDie(absl::string_view str);
+
+// As above, but do not S2_CHECK-fail on invalid input. Returns true if conversion
+// is successful.
+ABSL_MUST_USE_RESULT bool MakePoint(absl::string_view str, S2Point* point);
+
+ABSL_DEPRECATED("Use MakePointOrDie.")
+inline S2Point MakePoint(absl::string_view str) { return MakePointOrDie(str); }
+
+// Parses a string of one or more latitude-longitude coordinates in degrees,
+// and return the corresponding vector of S2LatLng points.
+// Examples of the input format:
+// "" // no points
+// "-20:150" // one point
+// "-20:150, -20:151, -19:150" // three points
+std::vector<S2LatLng> ParseLatLngsOrDie(absl::string_view str);
+
+// As above, but does not S2_CHECK-fail on invalid input. Returns true if
+// conversion is successful.
+ABSL_MUST_USE_RESULT bool ParseLatLngs(absl::string_view str,
+ std::vector<S2LatLng>* latlngs);
+
+ABSL_DEPRECATED("Use ParseLatLngsOrDie.")
+inline std::vector<S2LatLng> ParseLatLngs(absl::string_view str) {
+ return ParseLatLngsOrDie(str);
+}
+
+// Parses a string in the same format as ParseLatLngs, and return the
+// corresponding vector of S2Point values.
+std::vector<S2Point> ParsePointsOrDie(absl::string_view str);
+
+// As above, but does not S2_CHECK-fail on invalid input. Returns true if
+// conversion is successful.
+ABSL_MUST_USE_RESULT bool ParsePoints(absl::string_view str,
+ std::vector<S2Point>* vertices);
+
+ABSL_DEPRECATED("Use ParsePointsOrDie.")
+inline std::vector<S2Point> ParsePoints(absl::string_view str) {
+ return ParsePointsOrDie(str);
+}
+
+// Given a string in the same format as ParseLatLngs, returns a single S2LatLng.
+S2LatLng MakeLatLngOrDie(absl::string_view str);
+
+// As above, but does not S2_CHECK-fail on invalid input. Returns true if
+// conversion is successful.
+ABSL_MUST_USE_RESULT bool MakeLatLng(absl::string_view str, S2LatLng* latlng);
+
+// Given a string in the same format as ParseLatLngs, returns the minimal
+// bounding S2LatLngRect that contains the coordinates.
+S2LatLngRect MakeLatLngRectOrDie(absl::string_view str);
+
+// As above, but does not S2_CHECK-fail on invalid input. Returns true if
+// conversion is successful.
+ABSL_MUST_USE_RESULT bool MakeLatLngRect(absl::string_view str,
+ S2LatLngRect* rect);
+
+ABSL_DEPRECATED("Use MakeLatLngRectOrDie.")
+inline S2LatLngRect MakeLatLngRect(absl::string_view str) {
+ return MakeLatLngRectOrDie(str);
+}
+
+// Parses an S2CellId in the format "f/dd..d" where "f" is a digit in the
+// range [0-5] representing the S2CellId face, and "dd..d" is a string of
+// digits in the range [0-3] representing each child's position with respect
+// to its parent. (Note that the latter string may be empty.)
+//
+// For example "4/" represents S2CellId::FromFace(4), and "3/02" represents
+// S2CellId::FromFace(3).child(0).child(2).
+//
+// This function is a wrapper for S2CellId::FromDebugString().
+S2CellId MakeCellIdOrDie(absl::string_view str);
+
+// As above, but does not S2_CHECK-fail on invalid input. Returns true if
+// conversion is successful.
+ABSL_MUST_USE_RESULT bool MakeCellId(absl::string_view str, S2CellId* cell_id);
+
+// Parses a comma-separated list of S2CellIds in the format above, and returns
+// the corresponding S2CellUnion. (Note that S2CellUnions are automatically
+// normalized by sorting, removing duplicates, and replacing groups of 4 child
+// cells by their parent cell.)
+S2CellUnion MakeCellUnionOrDie(absl::string_view str);
+
+// As above, but does not S2_CHECK-fail on invalid input. Returns true if
+// conversion is successful.
+ABSL_MUST_USE_RESULT bool MakeCellUnion(absl::string_view str,
+ S2CellUnion* cell_union);
+
+// Given a string of latitude-longitude coordinates in degrees,
+// returns a newly allocated loop. Example of the input format:
+// "-20:150, 10:-120, 0.123:-170.652"
+// The strings "empty" or "full" create an empty or full loop respectively.
+std::unique_ptr<S2Loop> MakeLoopOrDie(absl::string_view str,
+ S2Debug debug_override = S2Debug::ALLOW);
+
+// As above, but does not S2_CHECK-fail on invalid input. Returns true if
+// conversion is successful.
+ABSL_MUST_USE_RESULT bool MakeLoop(absl::string_view str,
+ std::unique_ptr<S2Loop>* loop,
+ S2Debug debug_override = S2Debug::ALLOW);
+
+ABSL_DEPRECATED("Use MakeLoopOrDie.")
+std::unique_ptr<S2Loop> MakeLoop(absl::string_view str,
+ S2Debug debug_override = S2Debug::ALLOW);
+
+// Similar to MakeLoop(), but returns an S2Polyline rather than an S2Loop.
+std::unique_ptr<S2Polyline> MakePolylineOrDie(
+ absl::string_view str,
+ S2Debug debug_override = S2Debug::ALLOW);
+
+// As above, but does not S2_CHECK-fail on invalid input. Returns true if
+// conversion is successful.
+ABSL_MUST_USE_RESULT bool MakePolyline(absl::string_view str,
+ std::unique_ptr<S2Polyline>* polyline,
+ S2Debug debug_override = S2Debug::ALLOW);
+
+ABSL_DEPRECATED("Use MakePolylineOrDie.")
+std::unique_ptr<S2Polyline> MakePolyline(
+ absl::string_view str,
+ S2Debug debug_override = S2Debug::ALLOW);
+
+// Like MakePolyline, but returns an S2LaxPolylineShape instead.
+std::unique_ptr<S2LaxPolylineShape> MakeLaxPolylineOrDie(absl::string_view str);
+
+// As above, but does not S2_CHECK-fail on invalid input. Returns true if
+// conversion is successful.
+ABSL_MUST_USE_RESULT bool MakeLaxPolyline(
+ absl::string_view str, std::unique_ptr<S2LaxPolylineShape>* lax_polyline);
+
+ABSL_DEPRECATED("Use MakeLaxPolylineOrDie.")
+std::unique_ptr<S2LaxPolylineShape> MakeLaxPolyline(absl::string_view str);
+
+// Given a sequence of loops separated by semicolons, returns a newly
+// allocated polygon. Loops are automatically normalized by inverting them
+// if necessary so that they enclose at most half of the unit sphere.
+// (Historically this was once a requirement of polygon loops. It also
+// hides the problem that if the user thinks of the coordinates as X:Y
+// rather than LAT:LNG, it yields a loop with the opposite orientation.)
+//
+// Examples of the input format:
+// "10:20, 90:0, 20:30" // one loop
+// "10:20, 90:0, 20:30; 5.5:6.5, -90:-180, -15.2:20.3" // two loops
+// "" // the empty polygon (consisting of no loops)
+// "empty" // the empty polygon (consisting of no loops)
+// "full" // the full polygon (consisting of one full loop).
+std::unique_ptr<S2Polygon> MakePolygonOrDie(
+ absl::string_view str,
+ S2Debug debug_override = S2Debug::ALLOW);
+
+// As above, but does not S2_CHECK-fail on invalid input. Returns true if
+// conversion is successful.
+ABSL_MUST_USE_RESULT bool MakePolygon(absl::string_view str,
+ std::unique_ptr<S2Polygon>* polygon,
+ S2Debug debug_override = S2Debug::ALLOW);
+
+ABSL_DEPRECATED("Use MakePolygonOrDie.")
+std::unique_ptr<S2Polygon> MakePolygon(absl::string_view str,
+ S2Debug debug_override = S2Debug::ALLOW);
+
+// Like MakePolygon(), except that it does not normalize loops (i.e., it
+// gives you exactly what you asked for).
+std::unique_ptr<S2Polygon> MakeVerbatimPolygonOrDie(absl::string_view str);
+
+// As above, but does not S2_CHECK-fail on invalid input. Returns true if
+// conversion is successful.
+ABSL_MUST_USE_RESULT bool MakeVerbatimPolygon(
+ absl::string_view str, std::unique_ptr<S2Polygon>* polygon);
+
+ABSL_DEPRECATED("Use MakeVerbatimPolygonOrDie.")
+std::unique_ptr<S2Polygon> MakeVerbatimPolygon(absl::string_view str);
+
+// Parses a string in the same format as MakePolygon, except that loops must
+// be oriented so that the interior of the loop is always on the left, and
+// polygons with degeneracies are supported. As with MakePolygon, "full" and
+// denotes the full polygon and "" or "empty" denote the empty polygon.
+std::unique_ptr<S2LaxPolygonShape> MakeLaxPolygonOrDie(absl::string_view str);
+
+// As above, but does not S2_CHECK-fail on invalid input. Returns true if
+// conversion is successful.
+ABSL_MUST_USE_RESULT bool MakeLaxPolygon(
+ absl::string_view str, std::unique_ptr<S2LaxPolygonShape>* lax_polygon);
+
+ABSL_DEPRECATED("Use MakeLaxPolygonOrDie.")
+std::unique_ptr<S2LaxPolygonShape> MakeLaxPolygon(absl::string_view str);
+
+// Returns a MutableS2ShapeIndex containing the points, polylines, and loops
+// (in the form of a single polygon) described by the following format:
+//
+// point1|point2|... # line1|line2|... # polygon1|polygon2|...
+//
+// Examples:
+// 1:2 | 2:3 # # // Two points
+// # 0:0, 1:1, 2:2 | 3:3, 4:4 # // Two polylines
+// # # 0:0, 0:3, 3:0; 1:1, 2:1, 1:2 // Two nested loops (one polygon)
+// 5:5 # 6:6, 7:7 # 0:0, 0:1, 1:0 // One of each
+// # # empty // One empty polygon
+// # # empty | full // One empty polygon, one full polygon
+//
+// Loops should be directed so that the region's interior is on the left.
+// Loops can be degenerate (they do not need to meet S2Loop requirements).
+//
+// CAVEAT: Because whitespace is ignored, empty polygons must be specified
+// as the string "empty" rather than as the empty string ("").
+std::unique_ptr<MutableS2ShapeIndex> MakeIndexOrDie(absl::string_view str);
+
+// As above, but does not S2_CHECK-fail on invalid input. Returns true if
+// conversion is successful.
+ABSL_MUST_USE_RESULT bool MakeIndex(
+ absl::string_view str, std::unique_ptr<MutableS2ShapeIndex>* index);
+
+ABSL_DEPRECATED("Use MakeIndexOrDie.")
+std::unique_ptr<MutableS2ShapeIndex> MakeIndex(absl::string_view str);
+
+// Convert an S2Point, S2LatLng, S2LatLngRect, S2CellId, S2CellUnion, loop,
+// polyline, or polygon to the string format above.
+string ToString(const S2Point& point);
+string ToString(const S2LatLng& latlng);
+string ToString(const S2LatLngRect& rect);
+string ToString(const S2CellId& cell_id);
+string ToString(const S2CellUnion& cell_union);
+string ToString(const S2Loop& loop);
+string ToString(S2PointLoopSpan loop);
+string ToString(const S2Polyline& polyline);
+string ToString(const S2Polygon& polygon, const char* loop_separator = ";\n");
+string ToString(const std::vector<S2Point>& points);
+string ToString(const std::vector<S2LatLng>& points);
+string ToString(const S2LaxPolylineShape& polyline);
+string ToString(const S2LaxPolygonShape& polygon,
+ const char* loop_separator = ";\n");
+
+// Convert the contents of an S2ShapeIndex to the format above. The index may
+// contain S2Shapes of any type. Shapes are reordered if necessary so that
+// all point geometry (shapes of dimension 0) are first, followed by all
+// polyline geometry, followed by all polygon geometry.
+string ToString(const S2ShapeIndex& index);
+
+} // namespace s2textformat
+
+#endif // S2_S2TEXT_FORMAT_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// Defines functions for determining the relationship between two angles
+// ("wedges") that share a common vertex.
+
+#ifndef S2_S2WEDGE_RELATIONS_H_
+#define S2_S2WEDGE_RELATIONS_H_
+
+#include "s2/s2point.h"
+
+namespace S2 {
+
+// Given an edge chain (x0, x1, x2), the wedge at x1 is the region to the
+// left of the edges. More precisely, it is the set of all rays from x1x0
+// (inclusive) to x1x2 (exclusive) in the *clockwise* direction.
+//
+// The following functions compare two *non-empty* wedges that share the
+// same middle vertex: A=(a0, ab1, a2) and B=(b0, ab1, b2).
+
+// Detailed relation from one wedge A to another wedge B.
+enum WedgeRelation {
+ WEDGE_EQUALS, // A and B are equal.
+ WEDGE_PROPERLY_CONTAINS, // A is a strict superset of B.
+ WEDGE_IS_PROPERLY_CONTAINED, // A is a strict subset of B.
+ WEDGE_PROPERLY_OVERLAPS, // A-B, B-A, and A intersect B are non-empty.
+ WEDGE_IS_DISJOINT, // A and B are disjoint.
+};
+
+// Returns the relation from wedge A to B.
+// REQUIRES: A and B are non-empty.
+WedgeRelation GetWedgeRelation(
+ const S2Point& a0, const S2Point& ab1, const S2Point& a2,
+ const S2Point& b0, const S2Point& b2);
+
+// Returns true if wedge A contains wedge B. Equivalent to but faster than
+// GetWedgeRelation() == WEDGE_PROPERLY_CONTAINS || WEDGE_EQUALS.
+// REQUIRES: A and B are non-empty.
+bool WedgeContains(const S2Point& a0, const S2Point& ab1, const S2Point& a2,
+ const S2Point& b0, const S2Point& b2);
+
+// Returns true if wedge A intersects wedge B. Equivalent to but faster
+// than GetWedgeRelation() != WEDGE_IS_DISJOINT.
+// REQUIRES: A and B are non-empty.
+bool WedgeIntersects(const S2Point& a0, const S2Point& ab1, const S2Point& a2,
+ const S2Point& b0, const S2Point& b2);
+
+} // namespace S2
+
+#endif // S2_S2WEDGE_RELATIONS_H_
--- /dev/null
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_SEQUENCE_LEXICON_H_
+#define S2_SEQUENCE_LEXICON_H_
+
+#include <functional>
+#include <limits>
+#include <vector>
+
+#include "s2/base/integral_types.h"
+#include "s2/util/gtl/dense_hash_set.h"
+#include "s2/util/hash/mix.h"
+
+// SequenceLexicon is a class for compactly representing sequences of values
+// (e.g., tuples). It automatically eliminates duplicates, and maps the
+// remaining sequences to sequentially increasing integer ids. See also
+// ValueLexicon and IdSetLexicon.
+//
+// Each distinct sequence is mapped to a 32-bit integer. The space used for
+// each sequence is approximately 11 bytes plus the memory needed to represent
+// the sequence elements. For example, a sequence of three "double"s would
+// need about 11 + 3*8 = 35 bytes. Note also that sequences are referred to
+// using 32-bit ids rather than 64-bit pointers.
+//
+// This class has the same thread-safety properties as "string": const methods
+// are thread safe, and non-const methods are not thread safe.
+//
+// Example usage:
+//
+// SequenceLexicon<string> lexicon;
+// vector<string> pets {"cat", "dog", "parrot"};
+// uint32 pets_id = lexicon.Add(pets);
+// S2_CHECK_EQ(pets_id, lexicon.Add(pets));
+// string values;
+// for (const auto& pet : lexicon.sequence(pets_id)) {
+// values += pet;
+// }
+// S2_CHECK_EQ("catdogparrot", values);
+//
+template <class T,
+ class Hasher = std::hash<T>,
+ class KeyEqual = std::equal_to<T>>
+class SequenceLexicon {
+ public:
+ explicit SequenceLexicon(const Hasher& hasher = Hasher(),
+ const KeyEqual& key_equal = KeyEqual());
+
+ // SequenceLexicon is movable and copyable.
+ SequenceLexicon(const SequenceLexicon&);
+ SequenceLexicon& operator=(const SequenceLexicon&);
+ SequenceLexicon(SequenceLexicon&&);
+ SequenceLexicon& operator=(SequenceLexicon&&);
+
+ // Clears all data from the lexicon.
+ void Clear();
+
+ // Add the given sequence of values to the lexicon if it is not already
+ // present, and return its integer id. Ids are assigned sequentially
+ // starting from zero. "begin" and "end" are forward iterators over a
+ // sequence of values of type T.
+ template <class FwdIterator>
+ uint32 Add(FwdIterator begin, FwdIterator end);
+
+ // Add the given sequence of values to the lexicon if it is not already
+ // present, and return its integer id. Ids are assigned sequentially
+ // starting from zero. This is a convenience method equivalent to
+ // Add(std::begin(container), std::end(container)).
+ template <class Container>
+ uint32 Add(const Container& container);
+
+ // Return the number of value sequences in the lexicon.
+ uint32 size() const;
+
+ // Iterator type; please treat this as an opaque forward iterator.
+ using Iterator = typename std::vector<T>::const_iterator;
+
+ // A class representing a sequence of values.
+ class Sequence {
+ public:
+ Iterator begin() const { return begin_; }
+ Iterator end() const { return end_; }
+ size_t size() const { return end_ - begin_; }
+
+ private:
+ friend class SequenceLexicon;
+ Sequence(Iterator begin, Iterator end) : begin_(begin), end_(end) {}
+ Iterator begin_, end_;
+ };
+ // Return the value sequence with the given id. This method can be used
+ // with range-based for loops as follows:
+ // for (const auto& value : lexicon.sequence(id)) { ... }
+ Sequence sequence(uint32 id) const;
+
+ private:
+ friend class IdKeyEqual;
+ // Choose kEmptyKey to be the last key that will ever be generated.
+ static const uint32 kEmptyKey = std::numeric_limits<uint32>::max();
+
+ class IdHasher {
+ public:
+ IdHasher(const Hasher& hasher, const SequenceLexicon* lexicon);
+ const Hasher& hasher() const;
+ size_t operator()(uint32 id) const;
+ private:
+ Hasher hasher_;
+ const SequenceLexicon* lexicon_;
+ };
+
+ class IdKeyEqual {
+ public:
+ IdKeyEqual(const KeyEqual& key_equal, const SequenceLexicon* lexicon);
+ const KeyEqual& key_equal() const;
+ bool operator()(uint32 id1, uint32 id2) const;
+ private:
+ KeyEqual key_equal_;
+ const SequenceLexicon* lexicon_;
+ };
+
+ using IdSet = gtl::dense_hash_set<uint32, IdHasher, IdKeyEqual>;
+
+ std::vector<T> values_;
+ std::vector<uint32> begins_;
+ IdSet id_set_;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+template <class T, class Hasher, class KeyEqual>
+const uint32 SequenceLexicon<T, Hasher, KeyEqual>::kEmptyKey;
+
+template <class T, class Hasher, class KeyEqual>
+SequenceLexicon<T, Hasher, KeyEqual>::IdHasher::IdHasher(
+ const Hasher& hasher, const SequenceLexicon* lexicon)
+ : hasher_(hasher), lexicon_(lexicon) {
+}
+
+template <class T, class Hasher, class KeyEqual>
+const Hasher& SequenceLexicon<T, Hasher, KeyEqual>::IdHasher::hasher() const {
+ return hasher_;
+}
+
+template <class T, class Hasher, class KeyEqual>
+size_t SequenceLexicon<T, Hasher, KeyEqual>::IdHasher::operator()(
+ uint32 id) const {
+ HashMix mix;
+ for (const auto& value : lexicon_->sequence(id)) {
+ mix.Mix(hasher_(value));
+ }
+ return mix.get();
+}
+
+template <class T, class Hasher, class KeyEqual>
+SequenceLexicon<T, Hasher, KeyEqual>::IdKeyEqual::IdKeyEqual(
+ const KeyEqual& key_equal, const SequenceLexicon* lexicon)
+ : key_equal_(key_equal), lexicon_(lexicon) {
+}
+
+template <class T, class Hasher, class KeyEqual>
+const KeyEqual& SequenceLexicon<T, Hasher, KeyEqual>::IdKeyEqual::key_equal()
+ const {
+ return key_equal_;
+}
+
+template <class T, class Hasher, class KeyEqual>
+bool SequenceLexicon<T, Hasher, KeyEqual>::IdKeyEqual::operator()(
+ uint32 id1, uint32 id2) const {
+ if (id1 == id2) return true;
+ if (id1 == lexicon_->kEmptyKey || id2 == lexicon_->kEmptyKey) {
+ return false;
+ }
+ SequenceLexicon::Sequence seq1 = lexicon_->sequence(id1);
+ SequenceLexicon::Sequence seq2 = lexicon_->sequence(id2);
+ return (seq1.size() == seq2.size() &&
+ std::equal(seq1.begin(), seq1.end(), seq2.begin(), key_equal_));
+}
+
+template <class T, class Hasher, class KeyEqual>
+SequenceLexicon<T, Hasher, KeyEqual>::SequenceLexicon(const Hasher& hasher,
+ const KeyEqual& key_equal)
+ : id_set_(0, IdHasher(hasher, this),
+ IdKeyEqual(key_equal, this)) {
+ id_set_.set_empty_key(kEmptyKey);
+ begins_.push_back(0);
+}
+
+template <class T, class Hasher, class KeyEqual>
+SequenceLexicon<T, Hasher, KeyEqual>::SequenceLexicon(const SequenceLexicon& x)
+ : values_(x.values_), begins_(x.begins_),
+ // Unfortunately we can't copy "id_set_" because we need to change the
+ // "this" pointers associated with hasher() and key_equal().
+ id_set_(x.id_set_.begin(), x.id_set_.end(), kEmptyKey, 0,
+ IdHasher(x.id_set_.hash_funct().hasher(), this),
+ IdKeyEqual(x.id_set_.key_eq().key_equal(), this)) {
+}
+
+template <class T, class Hasher, class KeyEqual>
+SequenceLexicon<T, Hasher, KeyEqual>::SequenceLexicon(SequenceLexicon&& x)
+ : values_(std::move(x.values_)), begins_(std::move(x.begins_)),
+ // Unfortunately we can't move "id_set_" because we need to change the
+ // "this" pointers associated with hasher() and key_equal().
+ id_set_(x.id_set_.begin(), x.id_set_.end(), kEmptyKey, 0,
+ IdHasher(x.id_set_.hash_funct().hasher(), this),
+ IdKeyEqual(x.id_set_.key_eq().key_equal(), this)) {
+}
+
+template <class T, class Hasher, class KeyEqual>
+SequenceLexicon<T, Hasher, KeyEqual>&
+SequenceLexicon<T, Hasher, KeyEqual>::operator=(const SequenceLexicon& x) {
+ // Note that self-assignment is handled correctly by this code.
+ values_ = x.values_;
+ begins_ = x.begins_;
+ // Unfortunately we can't copy-assign "id_set_" because we need to change
+ // the "this" pointers associated with hasher() and key_equal().
+ id_set_ = IdSet(x.id_set_.begin(), x.id_set_.end(), kEmptyKey, 0,
+ IdHasher(x.id_set_.hash_funct().hasher(), this),
+ IdKeyEqual(x.id_set_.key_eq().key_equal(), this));
+ return *this;
+}
+
+template <class T, class Hasher, class KeyEqual>
+SequenceLexicon<T, Hasher, KeyEqual>&
+SequenceLexicon<T, Hasher, KeyEqual>::operator=(SequenceLexicon&& x) {
+ // Note that move self-assignment has undefined behavior.
+ values_ = std::move(x.values_);
+ begins_ = std::move(x.begins_);
+ // Unfortunately we can't move-assign "id_set_" because we need to change
+ // the "this" pointers associated with hasher() and key_equal().
+ id_set_ = IdSet(x.id_set_.begin(), x.id_set_.end(), kEmptyKey, 0,
+ IdHasher(x.id_set_.hash_funct().hasher(), this),
+ IdKeyEqual(x.id_set_.key_eq().key_equal(), this));
+ return *this;
+}
+
+template <class T, class Hasher, class KeyEqual>
+void SequenceLexicon<T, Hasher, KeyEqual>::Clear() {
+ values_.clear();
+ begins_.clear();
+ id_set_.clear();
+ begins_.push_back(0);
+}
+
+template <class T, class Hasher, class KeyEqual>
+template <class FwdIterator>
+uint32 SequenceLexicon<T, Hasher, KeyEqual>::Add(FwdIterator begin,
+ FwdIterator end) {
+ for (; begin != end; ++begin) {
+ values_.push_back(*begin);
+ }
+ begins_.push_back(values_.size());
+ uint32 id = begins_.size() - 2;
+ auto result = id_set_.insert(id);
+ if (result.second) {
+ return id;
+ } else {
+ begins_.pop_back();
+ values_.resize(begins_.back());
+ return *result.first;
+ }
+}
+
+template <class T, class Hasher, class KeyEqual>
+template <class Container>
+uint32 SequenceLexicon<T, Hasher, KeyEqual>::Add(const Container& container) {
+ return Add(std::begin(container), std::end(container));
+}
+
+template <class T, class Hasher, class KeyEqual>
+inline uint32 SequenceLexicon<T, Hasher, KeyEqual>::size() const {
+ return begins_.size() - 1;
+}
+
+template <class T, class Hasher, class KeyEqual>
+inline typename SequenceLexicon<T, Hasher, KeyEqual>::Sequence
+SequenceLexicon<T, Hasher, KeyEqual>::sequence(uint32 id) const {
+ return Sequence(values_.begin() + begins_[id],
+ values_.begin() + begins_[id + 1]);
+}
+
+#endif // S2_SEQUENCE_LEXICON_H_
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+
+
+#ifndef S2_STRINGS_OSTRINGSTREAM_H_
+#define S2_STRINGS_OSTRINGSTREAM_H_
+
+#include <ostream>
+#include <streambuf>
+#include <string>
+
+#include "s2/third_party/absl/base/port.h"
+
+namespace strings {
+
+// The same as std::ostringstream but appends to a user-specified string,
+// and is faster. It is ~70% faster to create, ~50% faster to write to, and
+// completely free to extract the result string.
+//
+// string s;
+// OStringStream strm(&s);
+// strm << 42 << ' ' << 3.14; // appends to `s`
+//
+// The stream object doesn't have to be named. Starting from C++11 operator<<
+// works with rvalues of std::ostream.
+//
+// string s;
+// OStringStream(&s) << 42 << ' ' << 3.14; // appends to `s`
+//
+// OStringStream is faster to create than std::ostringstream but it's still
+// relatively slow. Avoid creating multiple streams where a single stream will
+// do.
+//
+// Creates unnecessary instances of OStringStream: slow.
+//
+// string s;
+// OStringStream(&s) << 42;
+// OStringStream(&s) << ' ';
+// OStringStream(&s) << 3.14;
+//
+// Creates a single instance of OStringStream and reuses it: fast.
+//
+// string s;
+// OStringStream strm(&s);
+// strm << 42;
+// strm << ' ';
+// strm << 3.14;
+//
+// Note: flush() has no effect. No reason to call it.
+class OStringStream : private std::basic_streambuf<char>, public std::ostream {
+ public:
+ // Export the same types as ostringstream does; for use info, see
+ // http://en.cppreference.com/w/cpp/io/basic_ostringstream
+ typedef string::allocator_type allocator_type;
+
+ // These types are defined in both basic_streambuf and ostream, and although
+ // they are identical, they cause compiler ambiguities, so we define them to
+ // avoid that.
+ using std::ostream::char_type;
+ using std::ostream::int_type;
+ using std::ostream::off_type;
+ using std::ostream::pos_type;
+ using std::ostream::traits_type;
+
+ // The argument can be null, in which case you'll need to call str(p) with a
+ // non-null argument before you can write to the stream.
+ //
+ // The destructor of OStringStream doesn't use the string. It's OK to destroy
+ // the string before the stream.
+ explicit OStringStream(string* s) : std::ostream(this), s_(s) {}
+
+ string* str() { return s_; }
+ const string* str() const { return s_; }
+ void str(string* s) { s_ = s; }
+
+ // These functions are defined in both basic_streambuf and ostream, but it's
+ // ostream definition that affects the strings produced.
+ using std::ostream::getloc;
+ using std::ostream::imbue;
+
+ private:
+ using Buf = std::basic_streambuf<char>;
+
+ Buf::int_type overflow(int c) override;
+ std::streamsize xsputn(const char* s, std::streamsize n) override;
+
+ string* s_;
+};
+
+} // namespace strings
+
+#endif // S2_STRINGS_OSTRINGSTREAM_H_
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef S2_STRINGS_SERIALIZE_H_
+#define S2_STRINGS_SERIALIZE_H_
+
+#include <string>
+#include <vector>
+
+#include "s2/third_party/absl/strings/string_view.h"
+
+namespace strings {
+
+// -------------------------------------------------------------------------
+// DictionaryParse
+// This routine parses a common dictionary format (key and value separated
+// by ':', entries separated by commas). This format is used for many
+// complex commandline flags. It is also used to encode dictionaries for
+// exporting them or writing them to a checkpoint. Returns a vector of
+// <key, value> pairs. Returns true if there if no error in parsing, false
+// otherwise.
+// -------------------------------------------------------------------------
+bool DictionaryParse(absl::string_view encoded_str,
+ std::vector<std::pair<std::string, std::string>>* items);
+
+} // namespace strings
+
+#endif // S2_STRINGS_SERIALIZE_H_
--- /dev/null
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: algorithm.h
+// -----------------------------------------------------------------------------
+//
+// This header file contains Google extensions to the standard <algorithm> C++
+// header.
+
+#ifndef S2_THIRD_PARTY_ABSL_ALGORITHM_ALGORITHM_H_
+#define S2_THIRD_PARTY_ABSL_ALGORITHM_ALGORITHM_H_
+
+#include <algorithm>
+#include <iterator>
+#include <type_traits>
+
+namespace absl {
+
+namespace algorithm_internal {
+
+// Performs comparisons with operator==, similar to C++14's `std::equal_to<>`.
+struct EqualTo {
+ template <typename T, typename U>
+ bool operator()(const T& a, const U& b) const {
+ return a == b;
+ }
+};
+
+template <typename InputIter1, typename InputIter2, typename Pred>
+bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2,
+ InputIter2 last2, Pred pred, std::input_iterator_tag,
+ std::input_iterator_tag) {
+ while (true) {
+ if (first1 == last1) return first2 == last2;
+ if (first2 == last2) return false;
+ if (!pred(*first1, *first2)) return false;
+ ++first1;
+ ++first2;
+ }
+}
+
+template <typename InputIter1, typename InputIter2, typename Pred>
+bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2,
+ InputIter2 last2, Pred&& pred, std::random_access_iterator_tag,
+ std::random_access_iterator_tag) {
+ return (last1 - first1 == last2 - first2) &&
+ std::equal(first1, last1, first2, std::forward<Pred>(pred));
+}
+
+// When we are using our own internal predicate that just applies operator==, we
+// forward to the non-predicate form of std::equal. This enables an optimization
+// in libstdc++ that can result in std::memcmp being used for integer types.
+template <typename InputIter1, typename InputIter2>
+bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2,
+ InputIter2 last2, algorithm_internal::EqualTo /* unused */,
+ std::random_access_iterator_tag,
+ std::random_access_iterator_tag) {
+ return (last1 - first1 == last2 - first2) &&
+ std::equal(first1, last1, first2);
+}
+
+// clamp()
+//
+// Performs comparisons with operator<, similar to C++14's `std::less<void>`.
+struct Less {
+ template <typename T, typename U>
+ constexpr bool operator()(const T& a, const U& b) const {
+ return a < b;
+ }
+};
+
+template <typename It>
+It RotateImpl(It first, It middle, It last, std::true_type) {
+ return std::rotate(first, middle, last);
+}
+
+template <typename It>
+It RotateImpl(It first, It middle, It last, std::false_type) {
+ std::rotate(first, middle, last);
+ return std::next(first, std::distance(middle, last));
+}
+
+} // namespace algorithm_internal
+
+// clamp()
+//
+// Clamps the provided value to the range [low, high]. Returns a reference to
+// low if value is less than low, reference to high if high is less than value,
+// otherwise a reference to value.
+//
+// Requires comp(hi, lo) to be false. Invokes the comparison function at most
+// 2 times.
+//
+// Note that clamp returns a reference to its inputs, disabling temporary
+// lifetime extension, so passing a prvalue (including literals) and
+// subsequently assigning the result to a const reference is likely to be
+// incorrect.
+//
+// This is a C++11-compatible implementation of C++17 `std::clamp`. See
+// http://en.cppreference.com/w/cpp/algorithm/clamp for more information.
+template <typename T, typename Compare>
+constexpr const T& clamp(const T& v, const T& lo, const T& hi, Compare comp) {
+ return comp(v, lo) ? lo : comp(hi, v) ? hi : v;
+}
+
+// Requires T is LessThanComparable.
+template <typename T>
+constexpr const T& clamp(const T& v, const T& lo, const T& hi) {
+ return absl::clamp(v, lo, hi, algorithm_internal::Less{});
+}
+
+// Compares the equality of two ranges specified by pairs of iterators, using
+// the given predicate, returning true iff for each corresponding iterator i1
+// and i2 in the first and second range respectively, pred(*i1, *i2) == true
+//
+// This comparison takes at most min(`last1` - `first1`, `last2` - `first2`)
+// invocations of the predicate. Additionally, if InputIter1 and InputIter2 are
+// both random-access iterators, and `last1` - `first1` != `last2` - `first2`,
+// then the predicate is never invoked and the function returns false.
+//
+// This is a C++11-compatible implementation of C++14 `std::equal`. See
+// http://en.cppreference.com/w/cpp/algorithm/equal for more information.
+template <typename InputIter1, typename InputIter2, typename Pred>
+bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2,
+ InputIter2 last2, Pred&& pred) {
+ return algorithm_internal::EqualImpl(
+ first1, last1, first2, last2, std::forward<Pred>(pred),
+ typename std::iterator_traits<InputIter1>::iterator_category{},
+ typename std::iterator_traits<InputIter2>::iterator_category{});
+}
+
+// Performs comparison of two ranges specified by pairs of iterators using
+// operator==.
+template <typename InputIter1, typename InputIter2>
+bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2,
+ InputIter2 last2) {
+ return absl::equal(first1, last1, first2, last2,
+ algorithm_internal::EqualTo{});
+}
+
+// Performs a linear search for `value` using the iterator `first` up to
+// but not including `last`, returning true if [`first`, `last`) contains an
+// element equal to `value`.
+//
+// A linear search is of O(n) complexity which is guaranteed to make at most
+// n = (`last` - `first`) comparisons. A linear search over short containers
+// may be faster than a binary search, even when the container is sorted.
+template <typename InputIterator, typename EqualityComparable>
+bool linear_search(InputIterator first, InputIterator last,
+ const EqualityComparable& value) {
+ return std::find(first, last, value) != last;
+}
+
+// Performs a left rotation on a range of elements (`first`, `last`) such that
+// `middle` is now the first element. `rotate()` returns an iterator pointing to
+// the first element before rotation. This function is exactly the same as
+// `std::rotate`, but fixes a bug in gcc
+// <= 4.9 where `std::rotate` returns `void` instead of an iterator.
+//
+// The complexity of this algorithm is the same as that of `std::rotate`, but if
+// `ForwardIterator` is not a random-access iterator, then `absl::rotate`
+// performs an additional pass over the range to construct the return value.
+
+template <typename ForwardIterator>
+ForwardIterator rotate(ForwardIterator first, ForwardIterator middle,
+ ForwardIterator last) {
+ return algorithm_internal::RotateImpl(
+ first, middle, last,
+ std::is_same<decltype(std::rotate(first, middle, last)),
+ ForwardIterator>());
+}
+
+} // namespace absl
+
+#endif // S2_THIRD_PARTY_ABSL_ALGORITHM_ALGORITHM_H_
--- /dev/null
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// This header file defines macros for declaring attributes for functions,
+// types, and variables.
+//
+// These macros are used within Abseil and allow the compiler to optimize, where
+// applicable, certain function calls.
+//
+// This file is used for both C and C++!
+//
+// Most macros here are exposing GCC or Clang features, and are stubbed out for
+// other compilers.
+//
+// GCC attributes documentation:
+// https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html
+// https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Variable-Attributes.html
+// https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Type-Attributes.html
+//
+// Most attributes in this file are already supported by GCC 4.7. However, some
+// of them are not supported in older version of Clang. Thus, we check
+// `__has_attribute()` first. If the check fails, we check if we are on GCC and
+// assume the attribute exists on GCC (which is verified on GCC 4.7).
+//
+// -----------------------------------------------------------------------------
+// Sanitizer Attributes
+// -----------------------------------------------------------------------------
+//
+// Sanitizer-related attributes are not "defined" in this file (and indeed
+// are not defined as such in any file). To utilize the following
+// sanitizer-related attributes within your builds, define the following macros
+// within your build using a `-D` flag, along with the given value for
+// `-fsanitize`:
+//
+// * `ADDRESS_SANITIZER` + `-fsanitize=address` (Clang, GCC 4.8)
+// * `MEMORY_SANITIZER` + `-fsanitize=memory` (Clang-only)
+// * `THREAD_SANITIZER + `-fsanitize=thread` (Clang, GCC 4.8+)
+// * `UNDEFINED_BEHAVIOR_SANITIZER` + `-fsanitize=undefined` (Clang, GCC 4.9+)
+// * `CONTROL_FLOW_INTEGRITY` + -fsanitize=cfi (Clang-only)
+//
+// Example:
+//
+// // Enable branches in the Abseil code that are tagged for ASan:
+// $ bazel build --copt=-DADDRESS_SANITIZER --copt=-fsanitize=address
+// --linkopt=-fsanitize=address *target*
+//
+// Since these macro names are only supported by GCC and Clang, we only check
+// for `__GNUC__` (GCC or Clang) and the above macros.
+#ifndef S2_THIRD_PARTY_ABSL_BASE_ATTRIBUTES_H_
+#define S2_THIRD_PARTY_ABSL_BASE_ATTRIBUTES_H_
+
+#if defined(SWIG)
+
+#define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check)
+#define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check)
+#define ABSL_ATTRIBUTE_ALWAYS_INLINE
+#define ABSL_ATTRIBUTE_NOINLINE
+#define ABSL_ATTRIBUTE_NO_TAIL_CALL
+#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 0
+#define ABSL_ATTRIBUTE_WEAK
+#define ABSL_HAVE_ATTRIBUTE_WEAK 0
+#define ABSL_ATTRIBUTE_NONNULL(...)
+#define ABSL_ATTRIBUTE_NORETURN
+#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS
+#define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY
+#define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD
+#define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED
+#define ABSL_ATTRIBUTE_NO_SANITIZE_CFI
+#define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK
+#define ABSL_ATTRIBUTE_RETURNS_NONNULL
+#if SWIG_VERSION >= 0x020000
+#define ABSL_HAVE_ATTRIBUTE_SECTION 0
+#endif
+#define ABSL_ATTRIBUTE_SECTION(name)
+#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name)
+#define ABSL_INIT_ATTRIBUTE_SECTION_VARS(name)
+#define ABSL_DEFINE_ATTRIBUTE_SECTION_VARS(name)
+#define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name)
+#define ABSL_ATTRIBUTE_SECTION_START(name) (reinterpret_cast<void *>(0))
+#define ABSL_ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast<void *>(0))
+#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
+#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0)
+#define ABSL_MUST_USE_RESULT
+#define ABSL_ATTRIBUTE_HOT
+#define ABSL_ATTRIBUTE_COLD
+#define ABSL_ATTRIBUTE_REINITIALIZES
+#define ABSL_ATTRIBUTE_UNUSED
+#define ABSL_ATTRIBUTE_INITIAL_EXEC
+#define ABSL_ATTRIBUTE_PACKED
+#define ABSL_ATTRIBUTE_FUNC_ALIGN(bytes)
+
+// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
+// TODO(user): delete macros
+#define MUST_USE_RESULT
+
+#else // SWIG
+
+// ABSL_HAVE_ATTRIBUTE
+//
+// A function-like feature checking macro that is a wrapper around
+// `__has_attribute`, which is defined by GCC 5+ and Clang and evaluates to a
+// nonzero constant integer if the attribute is supported or 0 if not.
+//
+// It evaluates to zero if `__has_attribute` is not defined by the compiler.
+//
+// GCC: https://gcc.gnu.org/gcc-5/changes.html
+// Clang: https://clang.llvm.org/docs/LanguageExtensions.html
+#ifdef __has_attribute
+#define ABSL_HAVE_ATTRIBUTE(x) __has_attribute(x)
+#else
+#define ABSL_HAVE_ATTRIBUTE(x) 0
+#endif
+
+// ABSL_HAVE_CPP_ATTRIBUTE
+//
+// A function-like feature checking macro that accepts C++11 style attributes.
+// It's a wrapper around `__has_cpp_attribute`, defined by ISO C++ SD-6
+// (http://en.cppreference.com/w/cpp/experimental/feature_test). If we don't
+// find `__has_cpp_attribute`, will evaluate to 0.
+#if defined(__cplusplus) && defined(__has_cpp_attribute)
+// NOTE: requiring __cplusplus above should not be necessary, but
+// works around https://bugs.llvm.org/show_bug.cgi?id=23435.
+#define ABSL_HAVE_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
+#else
+#define ABSL_HAVE_CPP_ATTRIBUTE(x) 0
+#endif
+
+// -----------------------------------------------------------------------------
+// Function Attributes
+// -----------------------------------------------------------------------------
+//
+// GCC: https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
+// Clang: https://clang.llvm.org/docs/AttributeReference.html
+
+// ABSL_PRINTF_ATTRIBUTE
+// ABSL_SCANF_ATTRIBUTE
+//
+// Tells the compiler to perform `printf` format string checking if the
+// compiler supports it; see the 'format' attribute in
+// <http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html>.
+//
+// Note: As the GCC manual states, "[s]ince non-static C++ methods
+// have an implicit 'this' argument, the arguments of such methods
+// should be counted from two, not one."
+#if ABSL_HAVE_ATTRIBUTE(format) || (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check) \
+ __attribute__((__format__(__printf__, string_index, first_to_check)))
+#define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check) \
+ __attribute__((__format__(__scanf__, string_index, first_to_check)))
+#else
+#define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check)
+#define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check)
+#endif
+
+// ABSL_ATTRIBUTE_ALWAYS_INLINE
+// ABSL_ATTRIBUTE_NOINLINE
+//
+// Forces functions to either inline or not inline. Introduced in gcc 3.1.
+#if ABSL_HAVE_ATTRIBUTE(always_inline) || \
+ (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline))
+#define ABSL_HAVE_ATTRIBUTE_ALWAYS_INLINE 1
+#else
+#define ABSL_ATTRIBUTE_ALWAYS_INLINE
+#endif
+
+#if ABSL_HAVE_ATTRIBUTE(noinline) || (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_ATTRIBUTE_NOINLINE __attribute__((noinline))
+#define ABSL_HAVE_ATTRIBUTE_NOINLINE 1
+#else
+#define ABSL_ATTRIBUTE_NOINLINE
+#endif
+
+// ABSL_ATTRIBUTE_NO_TAIL_CALL
+//
+// Prevents the compiler from optimizing away stack frames for functions which
+// end in a call to another function.
+#if ABSL_HAVE_ATTRIBUTE(disable_tail_calls)
+#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1
+#define ABSL_ATTRIBUTE_NO_TAIL_CALL __attribute__((disable_tail_calls))
+#elif defined(__GNUC__) && !defined(__clang__)
+#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1
+#define ABSL_ATTRIBUTE_NO_TAIL_CALL \
+ __attribute__((optimize("no-optimize-sibling-calls")))
+#else
+#define ABSL_ATTRIBUTE_NO_TAIL_CALL
+#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 0
+#endif
+
+// ABSL_ATTRIBUTE_WEAK
+//
+// Tags a function as weak for the purposes of compilation and linking.
+// Weak attributes currently do not work properly in LLVM's Windows backend,
+// so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598
+// for futher information.
+#if (ABSL_HAVE_ATTRIBUTE(weak) || \
+ (defined(__GNUC__) && !defined(__clang__))) && \
+ !(defined(__llvm__) && defined(_WIN32))
+#undef ABSL_ATTRIBUTE_WEAK
+#define ABSL_ATTRIBUTE_WEAK __attribute__((weak))
+#define ABSL_HAVE_ATTRIBUTE_WEAK 1
+#else
+#define ABSL_ATTRIBUTE_WEAK
+#define ABSL_HAVE_ATTRIBUTE_WEAK 0
+#endif
+
+// ABSL_ATTRIBUTE_NONNULL
+//
+// Tells the compiler either (a) that a particular function parameter
+// should be a non-null pointer, or (b) that all pointer arguments should
+// be non-null.
+//
+// Note: As the GCC manual states, "[s]ince non-static C++ methods
+// have an implicit 'this' argument, the arguments of such methods
+// should be counted from two, not one."
+//
+// Args are indexed starting at 1.
+//
+// For non-static class member functions, the implicit `this` argument
+// is arg 1, and the first explicit argument is arg 2. For static class member
+// functions, there is no implicit `this`, and the first explicit argument is
+// arg 1.
+//
+// Example:
+//
+// /* arg_a cannot be null, but arg_b can */
+// void Function(void* arg_a, void* arg_b) ABSL_ATTRIBUTE_NONNULL(1);
+//
+// class C {
+// /* arg_a cannot be null, but arg_b can */
+// void Method(void* arg_a, void* arg_b) ABSL_ATTRIBUTE_NONNULL(2);
+//
+// /* arg_a cannot be null, but arg_b can */
+// static void StaticMethod(void* arg_a, void* arg_b)
+// ABSL_ATTRIBUTE_NONNULL(1);
+// };
+//
+// If no arguments are provided, then all pointer arguments should be non-null.
+//
+// /* No pointer arguments may be null. */
+// void Function(void* arg_a, void* arg_b, int arg_c) ABSL_ATTRIBUTE_NONNULL();
+//
+// NOTE: The GCC nonnull attribute actually accepts a list of arguments, but
+// ABSL_ATTRIBUTE_NONNULL does not.
+#if ABSL_HAVE_ATTRIBUTE(nonnull) || (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_ATTRIBUTE_NONNULL(arg_index) __attribute__((nonnull(arg_index)))
+#else
+#define ABSL_ATTRIBUTE_NONNULL(...)
+#endif
+
+// ABSL_ATTRIBUTE_NORETURN
+//
+// Tells the compiler that a given function never returns.
+#if ABSL_HAVE_ATTRIBUTE(noreturn) || (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_ATTRIBUTE_NORETURN __attribute__((noreturn))
+#elif defined(_MSC_VER)
+#define ABSL_ATTRIBUTE_NORETURN __declspec(noreturn)
+#else
+#define ABSL_ATTRIBUTE_NORETURN
+#endif
+
+// ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS
+//
+// Tells the AddressSanitizer (or other memory testing tools) to ignore a given
+// function. Useful for cases when a function reads random locations on stack,
+// calls _exit from a cloned subprocess, deliberately accesses buffer
+// out of bounds or does other scary things with memory.
+// NOTE: GCC supports AddressSanitizer(asan) since 4.8.
+// https://gcc.gnu.org/gcc-4.8/changes.html
+#if defined(__GNUC__) && defined(ADDRESS_SANITIZER)
+#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
+#else
+#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS
+#endif
+
+// ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY
+//
+// Tells the MemorySanitizer to relax the handling of a given function. All
+// "Use of uninitialized value" warnings from such functions will be suppressed,
+// and all values loaded from memory will be considered fully initialized.
+// This attribute is similar to the ADDRESS_SANITIZER attribute above, but deals
+// with initialized-ness rather than addressability issues.
+// NOTE: MemorySanitizer(msan) is supported by Clang but not GCC.
+#if defined(__GNUC__) && defined(MEMORY_SANITIZER)
+#define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory))
+#else
+#define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY
+#endif
+
+// ABSL_ATTRIBUTE_NO_SANITIZE_THREAD
+//
+// Tells the ThreadSanitizer to not instrument a given function.
+// If you are adding this attribute, please cc dynamic-tools@ on the cl.
+// NOTE: GCC supports ThreadSanitizer(tsan) since 4.8.
+// https://gcc.gnu.org/gcc-4.8/changes.html
+#if defined(__GNUC__) && defined(THREAD_SANITIZER)
+#define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
+#else
+#define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD
+#endif
+
+// ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED
+//
+// Tells the UndefinedSanitizer to ignore a given function. Useful for cases
+// where certain behavior (eg. division by zero) is being used intentionally.
+// NOTE: GCC supports UndefinedBehaviorSanitizer(ubsan) since 4.9.
+// https://gcc.gnu.org/gcc-4.9/changes.html
+#if defined(__GNUC__) && \
+ (defined(UNDEFINED_BEHAVIOR_SANITIZER) || defined(ADDRESS_SANITIZER))
+#define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED \
+ __attribute__((no_sanitize("undefined")))
+#else
+#define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED
+#endif
+
+// ABSL_ATTRIBUTE_NO_SANITIZE_CFI
+//
+// Tells the ControlFlowIntegrity sanitizer to not instrument a given function.
+// See https://clang.llvm.org/docs/ControlFlowIntegrity.html for details.
+#if defined(__GNUC__) && defined(CONTROL_FLOW_INTEGRITY)
+#define ABSL_ATTRIBUTE_NO_SANITIZE_CFI __attribute__((no_sanitize("cfi")))
+#else
+#define ABSL_ATTRIBUTE_NO_SANITIZE_CFI
+#endif
+
+// ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK
+//
+// Tells the SafeStack to not instrument a given function.
+// See https://clang.llvm.org/docs/SafeStack.html for details.
+#if defined(__GNUC__) && defined(SAFESTACK_SANITIZER)
+#define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK \
+ __attribute__((no_sanitize("safe-stack")))
+#else
+#define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK
+#endif
+
+// ABSL_ATTRIBUTE_RETURNS_NONNULL
+//
+// Tells the compiler that a particular function never returns a null pointer.
+#if ABSL_HAVE_ATTRIBUTE(returns_nonnull) || \
+ (defined(__GNUC__) && \
+ (__GNUC__ > 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) && \
+ !defined(__clang__))
+#define ABSL_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull))
+#else
+#define ABSL_ATTRIBUTE_RETURNS_NONNULL
+#endif
+
+// ABSL_HAVE_ATTRIBUTE_SECTION
+//
+// Indicates whether labeled sections are supported. Weak symbol support is
+// a prerequisite. Labeled sections are not supported on Darwin/iOS.
+#ifdef ABSL_HAVE_ATTRIBUTE_SECTION
+#error ABSL_HAVE_ATTRIBUTE_SECTION cannot be directly set
+#elif (ABSL_HAVE_ATTRIBUTE(section) || \
+ (defined(__GNUC__) && !defined(__clang__))) && \
+ !defined(__APPLE__) && ABSL_HAVE_ATTRIBUTE_WEAK
+#define ABSL_HAVE_ATTRIBUTE_SECTION 1
+
+// ABSL_ATTRIBUTE_SECTION
+//
+// Tells the compiler/linker to put a given function into a section and define
+// `__start_ ## name` and `__stop_ ## name` symbols to bracket the section.
+// This functionality is supported by GNU linker. Any function annotated with
+// `ABSL_ATTRIBUTE_SECTION` must not be inlined, or it will be placed into
+// whatever section its caller is placed into.
+//
+#ifndef ABSL_ATTRIBUTE_SECTION
+#define ABSL_ATTRIBUTE_SECTION(name) \
+ __attribute__((section(#name))) __attribute__((noinline))
+#endif
+
+
+// ABSL_ATTRIBUTE_SECTION_VARIABLE
+//
+// Tells the compiler/linker to put a given variable into a section and define
+// `__start_ ## name` and `__stop_ ## name` symbols to bracket the section.
+// This functionality is supported by GNU linker.
+#ifndef ABSL_ATTRIBUTE_SECTION_VARIABLE
+#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) __attribute__((section(#name)))
+#endif
+
+// ABSL_DECLARE_ATTRIBUTE_SECTION_VARS
+//
+// A weak section declaration to be used as a global declaration
+// for ABSL_ATTRIBUTE_SECTION_START|STOP(name) to compile and link
+// even without functions with ABSL_ATTRIBUTE_SECTION(name).
+// ABSL_DEFINE_ATTRIBUTE_SECTION should be in the exactly one file; it's
+// a no-op on ELF but not on Mach-O.
+//
+#ifndef ABSL_DECLARE_ATTRIBUTE_SECTION_VARS
+#define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) \
+ extern char __start_##name[] ABSL_ATTRIBUTE_WEAK; \
+ extern char __stop_##name[] ABSL_ATTRIBUTE_WEAK
+#endif
+#ifndef ABSL_DEFINE_ATTRIBUTE_SECTION_VARS
+#define ABSL_INIT_ATTRIBUTE_SECTION_VARS(name)
+#define ABSL_DEFINE_ATTRIBUTE_SECTION_VARS(name)
+#endif
+
+// ABSL_ATTRIBUTE_SECTION_START
+//
+// Returns `void*` pointers to start/end of a section of code with
+// functions having ABSL_ATTRIBUTE_SECTION(name).
+// Returns 0 if no such functions exist.
+// One must ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) for this to compile and
+// link.
+//
+#define ABSL_ATTRIBUTE_SECTION_START(name) \
+ (reinterpret_cast<void *>(__start_##name))
+#define ABSL_ATTRIBUTE_SECTION_STOP(name) \
+ (reinterpret_cast<void *>(__stop_##name))
+
+#else // !ABSL_HAVE_ATTRIBUTE_SECTION
+
+#define ABSL_HAVE_ATTRIBUTE_SECTION 0
+
+// provide dummy definitions
+#define ABSL_ATTRIBUTE_SECTION(name)
+#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name)
+#define ABSL_INIT_ATTRIBUTE_SECTION_VARS(name)
+#define ABSL_DEFINE_ATTRIBUTE_SECTION_VARS(name)
+#define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name)
+#define ABSL_ATTRIBUTE_SECTION_START(name) (reinterpret_cast<void *>(0))
+#define ABSL_ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast<void *>(0))
+
+#endif // ABSL_ATTRIBUTE_SECTION
+
+// ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
+//
+// Support for aligning the stack on 32-bit x86.
+#if ABSL_HAVE_ATTRIBUTE(force_align_arg_pointer) || \
+ (defined(__GNUC__) && !defined(__clang__))
+#if defined(__i386__)
+#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC \
+ __attribute__((force_align_arg_pointer))
+#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0)
+#elif defined(__x86_64__)
+#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (1)
+#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
+#else // !__i386__ && !__x86_64
+#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0)
+#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
+#endif // __i386__
+#else
+#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
+#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0)
+#endif
+
+// ABSL_MUST_USE_RESULT
+//
+// Tells the compiler to warn about unused results.
+//
+// When annotating a function, it must appear as the first part of the
+// declaration or definition. The compiler will warn if the return value from
+// such a function is unused:
+//
+// ABSL_MUST_USE_RESULT Sprocket* AllocateSprocket();
+// AllocateSprocket(); // Triggers a warning.
+//
+// When annotating a class, it is equivalent to annotating every function which
+// returns an instance.
+//
+// class ABSL_MUST_USE_RESULT Sprocket {};
+// Sprocket(); // Triggers a warning.
+//
+// Sprocket MakeSprocket();
+// MakeSprocket(); // Triggers a warning.
+//
+// Note that references and pointers are not instances:
+//
+// Sprocket* SprocketPointer();
+// SprocketPointer(); // Does *not* trigger a warning.
+//
+// ABSL_MUST_USE_RESULT allows using cast-to-void to suppress the unused result
+// warning. For that, warn_unused_result is used only for clang but not for gcc.
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425
+//
+// Note: past advice was to place the macro after the argument list.
+#if ABSL_HAVE_ATTRIBUTE(nodiscard)
+#define ABSL_MUST_USE_RESULT [[nodiscard]]
+#elif defined(__clang__) && ABSL_HAVE_ATTRIBUTE(warn_unused_result)
+#define ABSL_MUST_USE_RESULT __attribute__((warn_unused_result))
+#else
+#define ABSL_MUST_USE_RESULT
+#endif
+
+// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
+// TODO(user): delete macros
+#if ABSL_HAVE_ATTRIBUTE(warn_unused_result) || \
+ (defined(__GNUC__) && !defined(__clang__))
+#define MUST_USE_RESULT __attribute__ ((warn_unused_result))
+#else
+#define MUST_USE_RESULT
+#endif
+
+// ABSL_ATTRIBUTE_HOT, ABSL_ATTRIBUTE_COLD
+//
+// Tells GCC that a function is hot or cold. GCC can use this information to
+// improve static analysis, i.e. a conditional branch to a cold function
+// is likely to be not-taken.
+// This annotation is used for function declarations.
+//
+// Example:
+//
+// int foo() ABSL_ATTRIBUTE_HOT;
+#if ABSL_HAVE_ATTRIBUTE(hot) || (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_ATTRIBUTE_HOT __attribute__((hot))
+#else
+#define ABSL_ATTRIBUTE_HOT
+#endif
+
+#if ABSL_HAVE_ATTRIBUTE(cold) || (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_ATTRIBUTE_COLD __attribute__((cold))
+#else
+#define ABSL_ATTRIBUTE_COLD
+#endif
+
+// ABSL_XRAY_ALWAYS_INSTRUMENT, ABSL_XRAY_NEVER_INSTRUMENT, ABSL_XRAY_LOG_ARGS
+//
+// We define the ABSL_XRAY_ALWAYS_INSTRUMENT and ABSL_XRAY_NEVER_INSTRUMENT
+// macro used as an attribute to mark functions that must always or never be
+// instrumented by XRay. Currently, this is only supported in Clang/LLVM.
+//
+// For reference on the LLVM XRay instrumentation, see
+// http://llvm.org/docs/XRay.html.
+//
+// A function with the XRAY_ALWAYS_INSTRUMENT macro attribute in its declaration
+// will always get the XRay instrumentation sleds. These sleds may introduce
+// some binary size and runtime overhead and must be used sparingly.
+//
+// These attributes only take effect when the following conditions are met:
+//
+// * The file/target is built in at least C++11 mode, with a Clang compiler
+// that supports XRay attributes.
+// * The file/target is built with the -fxray-instrument flag set for the
+// Clang/LLVM compiler.
+// * The function is defined in the translation unit (the compiler honors the
+// attribute in either the definition or the declaration, and must match).
+//
+// There are cases when, even when building with XRay instrumentation, users
+// might want to control specifically which functions are instrumented for a
+// particular build using special-case lists provided to the compiler. These
+// special case lists are provided to Clang via the
+// -fxray-always-instrument=... and -fxray-never-instrument=... flags. The
+// attributes in source take precedence over these special-case lists.
+//
+// To disable the XRay attributes at build-time, users may define
+// ABSL_NO_XRAY_ATTRIBUTES. Do NOT define ABSL_NO_XRAY_ATTRIBUTES on specific
+// packages/targets, as this may lead to conflicting definitions of functions at
+// link-time.
+//
+#if ABSL_HAVE_CPP_ATTRIBUTE(clang::xray_always_instrument) && \
+ !defined(ABSL_NO_XRAY_ATTRIBUTES)
+#define ABSL_XRAY_ALWAYS_INSTRUMENT [[clang::xray_always_instrument]]
+#define ABSL_XRAY_NEVER_INSTRUMENT [[clang::xray_never_instrument]]
+#if ABSL_HAVE_CPP_ATTRIBUTE(clang::xray_log_args)
+#define ABSL_XRAY_LOG_ARGS(N) \
+ [[clang::xray_always_instrument, clang::xray_log_args(N)]]
+#else
+#define ABSL_XRAY_LOG_ARGS(N) [[clang::xray_always_instrument]]
+#endif
+#else
+#define ABSL_XRAY_ALWAYS_INSTRUMENT
+#define ABSL_XRAY_NEVER_INSTRUMENT
+#define ABSL_XRAY_LOG_ARGS(N)
+#endif
+
+// ABSL_ATTRIBUTE_REINITIALIZES
+//
+// Indicates that a member function reinitializes the entire object to a known
+// state, independent of the previous state of the object.
+//
+// The clang-tidy check bugprone-use-after-move allows member functions marked
+// with this attribute to be called on objects that have been moved from;
+// without the attribute, this would result in a use-after-move warning.
+#if ABSL_HAVE_CPP_ATTRIBUTE(clang::reinitializes)
+#define ABSL_ATTRIBUTE_REINITIALIZES [[clang::reinitializes]]
+#else
+#define ABSL_ATTRIBUTE_REINITIALIZES
+#endif
+
+// -----------------------------------------------------------------------------
+// Variable Attributes
+// -----------------------------------------------------------------------------
+
+// ABSL_ATTRIBUTE_UNUSED
+//
+// Prevents the compiler from complaining about variables that appear unused.
+#if ABSL_HAVE_ATTRIBUTE(unused) || (defined(__GNUC__) && !defined(__clang__))
+#undef ABSL_ATTRIBUTE_UNUSED
+#define ABSL_ATTRIBUTE_UNUSED __attribute__((__unused__))
+#else
+#define ABSL_ATTRIBUTE_UNUSED
+#endif
+
+// ABSL_ATTRIBUTE_INITIAL_EXEC
+//
+// Tells the compiler to use "initial-exec" mode for a thread-local variable.
+// See http://people.redhat.com/drepper/tls.pdf for the gory details.
+#if ABSL_HAVE_ATTRIBUTE(tls_model) || (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_ATTRIBUTE_INITIAL_EXEC __attribute__((tls_model("initial-exec")))
+#else
+#define ABSL_ATTRIBUTE_INITIAL_EXEC
+#endif
+
+// ABSL_ATTRIBUTE_PACKED
+//
+// Prevents the compiler from padding a structure to natural alignment
+#if ABSL_HAVE_ATTRIBUTE(packed) || (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_ATTRIBUTE_PACKED __attribute__((__packed__))
+#else
+#define ABSL_ATTRIBUTE_PACKED
+#endif
+
+// ABSL_ATTRIBUTE_FUNC_ALIGN
+//
+// Tells the compiler to align the function start at least to certain
+// alignment boundary
+#if ABSL_HAVE_ATTRIBUTE(aligned) || (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_ATTRIBUTE_FUNC_ALIGN(bytes) __attribute__((aligned(bytes)))
+#else
+#define ABSL_ATTRIBUTE_FUNC_ALIGN(bytes)
+#endif
+
+#endif // SWIG
+
+// ABSL_CONST_INIT
+//
+// A variable declaration annotated with the `ABSL_CONST_INIT` attribute will
+// not compile (on supported platforms) unless the variable has a constant
+// initializer. This is useful for variables with static and thread storage
+// duration, because it guarantees that they will not suffer from the so-called
+// "static init order fiasco". Prefer to put this attribute on the most visible
+// declaration of the variable, if there's more than one, because code that
+// accesses the variable can then use the attribute for optimization.
+//
+// Example:
+//
+// class MyClass {
+// public:
+// ABSL_CONST_INIT static MyType my_var;
+// };
+//
+// MyType MyClass::my_var = MakeMyType(...);
+//
+// Note that this attribute is redundant if the variable is declared constexpr.
+#if ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization)
+// NOLINTNEXTLINE(whitespace/braces)
+#define ABSL_CONST_INIT [[clang::require_constant_initialization]]
+#else
+#define ABSL_CONST_INIT
+#endif // ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization)
+
+#endif // S2_THIRD_PARTY_ABSL_BASE_ATTRIBUTES_H_
--- /dev/null
+//
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: casts.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines casting templates to fit use cases not covered by
+// the standard casts provided in the C++ standard. As with all cast operations,
+// use these with caution and only if alternatives do not exist.
+
+#ifndef S2_THIRD_PARTY_ABSL_BASE_CASTS_H_
+#define S2_THIRD_PARTY_ABSL_BASE_CASTS_H_
+
+#include <cstring>
+#include <memory>
+#include <type_traits>
+
+#include "s2/third_party/absl/base/internal/identity.h"
+#include "s2/third_party/absl/base/macros.h"
+
+namespace absl {
+
+namespace internal_casts {
+
+// NOTE: Not a fully compliant implementation of `std::is_trivially_copyable`.
+// TODO(user) Branch on implementations that directly provide
+// `std::is_trivially_copyable`, create a more rigorous workaround, and publicly
+// expose in meta/type_traits.
+template <class T>
+struct is_trivially_copyable
+ : std::integral_constant<
+ bool, std::is_destructible<T>::value&& __has_trivial_destructor(T) &&
+ __has_trivial_copy(T) && __has_trivial_assign(T)> {};
+
+template <class Dest, class Source>
+struct is_bitcastable
+ : std::integral_constant<bool,
+ sizeof(Dest) == sizeof(Source) &&
+ is_trivially_copyable<Source>::value &&
+ is_trivially_copyable<Dest>::value &&
+ std::is_default_constructible<Dest>::value> {};
+
+} // namespace internal_casts
+
+// implicit_cast()
+//
+// Performs an implicit conversion between types following the language
+// rules for implicit conversion; if an implicit conversion is otherwise
+// allowed by the language in the given context, this function performs such an
+// implicit conversion.
+//
+// Example:
+//
+// // If the context allows implicit conversion:
+// From from;
+// To to = from;
+//
+// // Such code can be replaced by:
+// implicit_cast<To>(from);
+//
+// An `implicit_cast()` may also be used to annotate numeric type conversions
+// that, although safe, may produce compiler warnings (such as `long` to `int`).
+// Additionally, an `implicit_cast()` is also useful within return statements to
+// indicate a specific implicit conversion is being undertaken.
+//
+// Example:
+//
+// return implicit_cast<double>(size_in_bytes) / capacity_;
+//
+// Annotating code with `implicit_cast()` allows you to explicitly select
+// particular overloads and template instantiations, while providing a safer
+// cast than `reinterpret_cast()` or `static_cast()`.
+//
+// Additionally, an `implicit_cast()` can be used to allow upcasting within a
+// type hierarchy where incorrect use of `static_cast()` could accidentally
+// allow downcasting.
+//
+// Finally, an `implicit_cast()` can be used to perform implicit conversions
+// from unrelated types that otherwise couldn't be implicitly cast directly;
+// C++ will normally only implicitly cast "one step" in such conversions.
+//
+// That is, if C is a type which can be implicitly converted to B, with B being
+// a type that can be implicitly converted to A, an `implicit_cast()` can be
+// used to convert C to B (which the compiler can then implicitly convert to A
+// using language rules).
+//
+// Example:
+//
+// // Assume an object C is convertible to B, which is implicitly convertible
+// // to A
+// A a = implicit_cast<B>(C);
+//
+// Such implicit cast chaining may be useful within template logic.
+template <typename To>
+constexpr To implicit_cast(typename absl::internal::identity_t<To> to) {
+ return to;
+}
+
+// bit_cast()
+//
+// Performs a bitwise cast on a type without changing the underlying bit
+// representation of that type's value. The two types must be of the same size
+// and both types must be trivially copyable. As with most casts, use with
+// caution. A `bit_cast()` might be needed when you need to temporarily treat a
+// type as some other type, such as in the following cases:
+//
+// * Serialization (casting temporarily to `char *` for those purposes is
+// always allowed by the C++ standard)
+// * Managing the individual bits of a type within mathematical operations
+// that are not normally accessible through that type
+// * Casting non-pointer types to pointer types (casting the other way is
+// allowed by `reinterpret_cast()` but round-trips cannot occur the other
+// way).
+//
+// Example:
+//
+// float f = 3.14159265358979;
+// int i = bit_cast<int32>(f);
+// // i = 0x40490fdb
+//
+// Casting non-pointer types to pointer types and then dereferencing them
+// traditionally produces undefined behavior.
+//
+// Example:
+//
+// // WRONG
+// float f = 3.14159265358979; // WRONG
+// int i = * reinterpret_cast<int*>(&f); // WRONG
+//
+// The address-casting method produces undefined behavior according to the ISO
+// C++ specification section [basic.lval]. Roughly, this section says: if an
+// object in memory has one type, and a program accesses it with a different
+// type, the result is undefined behavior for most values of "different type".
+//
+// Such casting results in type punning: holding an object in memory of one type
+// and reading its bits back using a different type. A `bit_cast()` avoids this
+// issue by implementing its casts using `memcpy()`, which avoids introducing
+// this undefined behavior.
+//
+// NOTE: The requirements here are more strict than the bit_cast of standard
+// proposal p0476 due to the need for workarounds and lack of intrinsics.
+// Specifically, this implementation also requires `Dest` to be
+// default-constructible.
+template <
+ typename Dest, typename Source,
+ typename std::enable_if<internal_casts::is_bitcastable<Dest, Source>::value,
+ int>::type = 0>
+inline Dest bit_cast(const Source& source) {
+ Dest dest;
+ memcpy(static_cast<void*>(std::addressof(dest)),
+ static_cast<const void*>(std::addressof(source)), sizeof(dest));
+ return dest;
+}
+
+// NOTE: This overload is only picked if the requirements of bit_cast are not
+// met. It is therefore UB, but is provided temporarily as previous versions of
+// this function template were unchecked. Do not use this in new code.
+template <
+ typename Dest, typename Source,
+ typename std::enable_if<
+ !internal_casts::is_bitcastable<Dest, Source>::value, int>::type = 0>
+ABSL_DEPRECATED(
+ "absl::bit_cast type requirements were violated. Update the types being "
+ "used such that they are the same size and are both TriviallyCopyable.")
+inline Dest bit_cast(const Source& source) {
+ static_assert(sizeof(Dest) == sizeof(Source),
+ "Source and destination types should have equal sizes.");
+
+ Dest dest;
+ memcpy(&dest, &source, sizeof(dest));
+ return dest;
+}
+
+} // namespace absl
+
+#endif // S2_THIRD_PARTY_ABSL_BASE_CASTS_H_
--- /dev/null
+//
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: config.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines a set of macros for checking the presence of
+// important compiler and platform features. Such macros can be used to
+// produce portable code by parameterizing compilation based on the presence or
+// lack of a given feature.
+//
+// We define a "feature" as some interface we wish to program to: for example,
+// a library function or system call. A value of `1` indicates support for
+// that feature; any other value indicates the feature support is undefined.
+//
+// Example:
+//
+// Suppose a programmer wants to write a program that uses the 'mmap()' system
+// call. The Abseil macro for that feature (`ABSL_HAVE_MMAP`) allows you to
+// selectively include the `mmap.h` header and bracket code using that feature
+// in the macro:
+//
+// #include "absl/base/config.h"
+//
+// #ifdef ABSL_HAVE_MMAP
+// #include "sys/mman.h"
+// #endif //ABSL_HAVE_MMAP
+//
+// ...
+// #ifdef ABSL_HAVE_MMAP
+// void *ptr = mmap(...);
+// ...
+// #endif // ABSL_HAVE_MMAP
+
+#ifndef S2_THIRD_PARTY_ABSL_BASE_CONFIG_H_
+#define S2_THIRD_PARTY_ABSL_BASE_CONFIG_H_
+
+// Included for the __GLIBC__ macro (or similar macros on other systems).
+#include <climits>
+
+#ifdef __cplusplus
+// Included for __GLIBCXX__, _LIBCPP_VERSION
+#include <cstddef>
+#endif // __cplusplus
+
+#if defined(__APPLE__)
+// Included for TARGET_OS_IPHONE, __IPHONE_OS_VERSION_MIN_REQUIRED,
+// __IPHONE_8_0.
+#include <Availability.h>
+#include <TargetConditionals.h>
+#endif
+
+#include "s2/third_party/absl/base/policy_checks.h"
+
+// -----------------------------------------------------------------------------
+// Compiler Feature Checks
+// -----------------------------------------------------------------------------
+
+// ABSL_HAVE_BUILTIN()
+//
+// Checks whether the compiler supports a Clang Feature Checking Macro, and if
+// so, checks whether it supports the provided builtin function "x" where x
+// is one of the functions noted in
+// https://clang.llvm.org/docs/LanguageExtensions.html
+//
+// Note: Use this macro to avoid an extra level of #ifdef __has_builtin check.
+// http://releases.llvm.org/3.3/tools/clang/docs/LanguageExtensions.html
+#ifdef __has_builtin
+#define ABSL_HAVE_BUILTIN(x) __has_builtin(x)
+#else
+#define ABSL_HAVE_BUILTIN(x) 0
+#endif
+
+// ABSL_HAVE_TLS is defined to 1 when __thread should be supported.
+// We assume __thread is supported on Linux when compiled with Clang or compiled
+// against libstdc++ with _GLIBCXX_HAVE_TLS defined.
+#ifdef ABSL_HAVE_TLS
+#error ABSL_HAVE_TLS cannot be directly set
+#elif defined(__linux__) && (defined(__clang__) || defined(_GLIBCXX_HAVE_TLS))
+#define ABSL_HAVE_TLS 1
+#endif
+
+// ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
+//
+// Checks whether `std::is_trivially_destructible<T>` is supported.
+//
+// Notes: All supported compilers using libc++ support this feature, as does
+// gcc >= 4.8.1 using libstdc++, and Visual Studio.
+#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
+#error ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE cannot be directly set
+#elif defined(_LIBCPP_VERSION) || \
+ (!defined(__clang__) && defined(__GNUC__) && defined(__GLIBCXX__) && \
+ (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || \
+ defined(_MSC_VER)
+#define ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE 1
+#endif
+
+// ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
+//
+// Checks whether `std::is_trivially_default_constructible<T>` and
+// `std::is_trivially_copy_constructible<T>` are supported.
+
+// ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
+//
+// Checks whether `std::is_trivially_copy_assignable<T>` is supported.
+
+// Notes: Clang with libc++ supports these features, as does gcc >= 5.1 with
+// either libc++ or libstdc++, and Visual Studio.
+#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE)
+#error ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE cannot be directly set
+#elif defined(ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE)
+#error ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE cannot directly set
+#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) || \
+ (!defined(__clang__) && defined(__GNUC__) && \
+ (__GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)) && \
+ (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) || \
+ defined(_MSC_VER)
+#define ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1
+#define ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1
+#endif
+
+// ABSL_HAVE_THREAD_LOCAL
+//
+// Checks whether C++11's `thread_local` storage duration specifier is
+// supported.
+#ifdef ABSL_HAVE_THREAD_LOCAL
+#error ABSL_HAVE_THREAD_LOCAL cannot be directly set
+#elif defined(__APPLE__)
+// Notes:
+// * Xcode's clang did not support `thread_local` until version 8, and
+// even then not for all iOS < 9.0.
+// * Xcode 9.3 started disallowing `thread_local` for 32-bit iOS simulator
+// targeting iOS 9.x.
+// * Xcode 10 moves the deployment target check for iOS < 9.0 to link time
+// making __has_feature unreliable there.
+//
+// Otherwise, `__has_feature` is only supported by Clang so it has be inside
+// `defined(__APPLE__)` check.
+#if __has_feature(cxx_thread_local) && \
+ !(TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0)
+#define ABSL_HAVE_THREAD_LOCAL 1
+#endif
+#else // !defined(__APPLE__)
+#define ABSL_HAVE_THREAD_LOCAL 1
+#endif
+
+// There are platforms for which TLS should not be used even though the compiler
+// makes it seem like it's supported (Android NDK < r12b for example).
+// This is primarily because of linker problems and toolchain misconfiguration:
+// Abseil does not intend to support this indefinitely. Currently, the newest
+// toolchain that we intend to support that requires this behavior is the
+// r11 NDK - allowing for a 5 year support window on that means this option
+// is likely to be removed around June of 2021.
+// TLS isn't supported until NDK r12b per
+// https://developer.android.com/ndk/downloads/revision_history.html
+// Since NDK r16, `__NDK_MAJOR__` and `__NDK_MINOR__` are defined in
+// <android/ndk-version.h>. For NDK < r16, users should define these macros,
+// e.g. `-D__NDK_MAJOR__=11 -D__NKD_MINOR__=0` for NDK r11.
+#if defined(__ANDROID__) && defined(__clang__)
+#if __has_include(<android/ndk-version.h>)
+#include <android/ndk-version.h>
+#endif // __has_include(<android/ndk-version.h>)
+#if defined(__ANDROID__) && defined(__clang__) && defined(__NDK_MAJOR__) && \
+ defined(__NDK_MINOR__) && \
+ ((__NDK_MAJOR__ < 12) || ((__NDK_MAJOR__ == 12) && (__NDK_MINOR__ < 1)))
+#undef ABSL_HAVE_TLS
+#undef ABSL_HAVE_THREAD_LOCAL
+#endif
+#endif // defined(__ANDROID__) && defined(__clang__)
+
+// ABSL_HAVE_INTRINSIC_INT128
+//
+// Checks whether the __int128 compiler extension for a 128-bit integral type is
+// supported.
+//
+// Note: __SIZEOF_INT128__ is defined by Clang and GCC when __int128 is
+// supported, but we avoid using it in certain cases:
+// * On Clang:
+// * Building using Clang for Windows, where the Clang runtime library has
+// 128-bit support only on LP64 architectures, but Windows is LLP64.
+// * Building for aarch64, where __int128 exists but has exhibits a sporadic
+// compiler crashing bug.
+// * On Nvidia's nvcc:
+// * nvcc also defines __GNUC__ and __SIZEOF_INT128__, but not all versions
+// actually support __int128.
+#ifdef ABSL_HAVE_INTRINSIC_INT128
+#error ABSL_HAVE_INTRINSIC_INT128 cannot be directly set
+#elif defined(__SIZEOF_INT128__)
+#if (defined(__clang__) && !defined(_WIN32) && !defined(__aarch64__)) || \
+ (defined(__CUDACC__) && __CUDACC_VER_MAJOR__ >= 9) || \
+ (defined(__GNUC__) && !defined(__clang__) && !defined(__CUDACC__))
+#define ABSL_HAVE_INTRINSIC_INT128 1
+#elif defined(__CUDACC__)
+// __CUDACC_VER__ is a full version number before CUDA 9, and is defined to a
+// string explaining that it has been removed starting with CUDA 9. We use
+// nested #ifs because there is no short-circuiting in the preprocessor.
+// NOTE: `__CUDACC__` could be undefined while `__CUDACC_VER__` is defined.
+#if __CUDACC_VER__ >= 70000
+#define ABSL_HAVE_INTRINSIC_INT128 1
+#endif // __CUDACC_VER__ >= 70000
+#endif // defined(__CUDACC__)
+#endif // ABSL_HAVE_INTRINSIC_INT128
+
+// ABSL_HAVE_EXCEPTIONS
+//
+// Checks whether the compiler both supports and enables exceptions. Many
+// compilers support a "no exceptions" mode that disables exceptions.
+//
+// Generally, when ABSL_HAVE_EXCEPTIONS is not defined:
+//
+// * Code using `throw` and `try` may not compile.
+// * The `noexcept` specifier will still compile and behave as normal.
+// * The `noexcept` operator may still return `false`.
+//
+// For further details, consult the compiler's documentation.
+#ifdef ABSL_HAVE_EXCEPTIONS
+#error ABSL_HAVE_EXCEPTIONS cannot be directly set.
+
+#elif defined(__clang__)
+// TODO(user)
+// Switch to using __cpp_exceptions when we no longer support versions < 3.6.
+// For details on this check, see:
+// http://releases.llvm.org/3.6.0/tools/clang/docs/ReleaseNotes.html#the-exceptions-macro
+#if defined(__EXCEPTIONS) && __has_feature(cxx_exceptions)
+#define ABSL_HAVE_EXCEPTIONS 1
+#endif // defined(__EXCEPTIONS) && __has_feature(cxx_exceptions)
+
+// Handle remaining special cases and default to exceptions being supported.
+#elif !(defined(__GNUC__) && (__GNUC__ < 5) && !defined(__EXCEPTIONS)) && \
+ !(defined(__GNUC__) && (__GNUC__ >= 5) && !defined(__cpp_exceptions)) && \
+ !(defined(_MSC_VER) && !defined(_CPPUNWIND))
+#define ABSL_HAVE_EXCEPTIONS 1
+#endif
+
+// -----------------------------------------------------------------------------
+// Platform Feature Checks
+// -----------------------------------------------------------------------------
+
+// Currently supported operating systems and associated preprocessor
+// symbols:
+//
+// Linux and Linux-derived __linux__
+// Android __ANDROID__ (implies __linux__)
+// Linux (non-Android) __linux__ && !__ANDROID__
+// Darwin (Mac OS X and iOS) __APPLE__
+// Akaros (http://akaros.org) __ros__
+// Windows _WIN32
+// NaCL __native_client__
+// AsmJS __asmjs__
+// WebAssembly __wasm__
+// Fuchsia __Fuchsia__
+//
+// Note that since Android defines both __ANDROID__ and __linux__, one
+// may probe for either Linux or Android by simply testing for __linux__.
+
+// ABSL_HAVE_MMAP
+//
+// Checks whether the platform has an mmap(2) implementation as defined in
+// POSIX.1-2001.
+#ifdef ABSL_HAVE_MMAP
+#error ABSL_HAVE_MMAP cannot be directly set
+#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
+ defined(__ros__) || defined(__native_client__) || defined(__asmjs__) || \
+ defined(__wasm__) || defined(__Fuchsia__) || defined(__sun) || \
+ defined(__ASYLO__)
+#define ABSL_HAVE_MMAP 1
+#endif
+
+// ABSL_HAVE_PTHREAD_GETSCHEDPARAM
+//
+// Checks whether the platform implements the pthread_(get|set)schedparam(3)
+// functions as defined in POSIX.1-2001.
+#ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM
+#error ABSL_HAVE_PTHREAD_GETSCHEDPARAM cannot be directly set
+#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
+ defined(__ros__)
+#define ABSL_HAVE_PTHREAD_GETSCHEDPARAM 1
+#endif
+
+// ABSL_HAVE_SCHED_YIELD
+//
+// Checks whether the platform implements sched_yield(2) as defined in
+// POSIX.1-2001.
+#ifdef ABSL_HAVE_SCHED_YIELD
+#error ABSL_HAVE_SCHED_YIELD cannot be directly set
+#elif defined(__linux__) || defined(__ros__) || defined(__native_client__)
+#define ABSL_HAVE_SCHED_YIELD 1
+#endif
+
+// ABSL_HAVE_SEMAPHORE_H
+//
+// Checks whether the platform supports the <semaphore.h> header and sem_open(3)
+// family of functions as standardized in POSIX.1-2001.
+//
+// Note: While Apple provides <semaphore.h> for both iOS and macOS, it is
+// explicitly deprecated and will cause build failures if enabled for those
+// platforms. We side-step the issue by not defining it here for Apple
+// platforms.
+#ifdef ABSL_HAVE_SEMAPHORE_H
+#error ABSL_HAVE_SEMAPHORE_H cannot be directly set
+#elif defined(__linux__) || defined(__ros__)
+#define ABSL_HAVE_SEMAPHORE_H 1
+#endif
+
+// ABSL_HAVE_ALARM
+//
+// Checks whether the platform supports the <signal.h> header and alarm(2)
+// function as standardized in POSIX.1-2001.
+#ifdef ABSL_HAVE_ALARM
+#error ABSL_HAVE_ALARM cannot be directly set
+#elif defined(__GOOGLE_GRTE_VERSION__)
+// feature tests for Google's GRTE
+#define ABSL_HAVE_ALARM 1
+#elif defined(__GLIBC__)
+// feature test for glibc
+#define ABSL_HAVE_ALARM 1
+#elif defined(_MSC_VER)
+// feature tests for Microsoft's library
+#elif defined(__EMSCRIPTEN__)
+// emscripten doesn't support signals
+#elif defined(__native_client__)
+#else
+// other standard libraries
+#define ABSL_HAVE_ALARM 1
+#endif
+
+// ABSL_IS_LITTLE_ENDIAN
+// ABSL_IS_BIG_ENDIAN
+//
+// Checks the endianness of the platform.
+//
+// Notes: uses the built in endian macros provided by GCC (since 4.6) and
+// Clang (since 3.2); see
+// https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html.
+// Otherwise, if _WIN32, assume little endian. Otherwise, bail with an error.
+#if defined(ABSL_IS_BIG_ENDIAN)
+#error "ABSL_IS_BIG_ENDIAN cannot be directly set."
+#endif
+#if defined(ABSL_IS_LITTLE_ENDIAN)
+#error "ABSL_IS_LITTLE_ENDIAN cannot be directly set."
+#endif
+
+#if (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
+ __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+#define ABSL_IS_LITTLE_ENDIAN 1
+#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
+ __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define ABSL_IS_BIG_ENDIAN 1
+#elif defined(_WIN32)
+#define ABSL_IS_LITTLE_ENDIAN 1
+#else
+#error "absl endian detection needs to be set up for your compiler"
+#endif
+
+// MacOS 10.13 doesn't let you use <any>, <optional>, or <variant> even though
+// the headers exist and are publicly noted to work. See
+// https://github.com/abseil/abseil-cpp/issues/207 and
+// https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes
+#if defined(__APPLE__) && defined(_LIBCPP_VERSION) && \
+ defined(__MAC_OS_X_VERSION_MIN_REQUIRED__) && \
+ __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101400
+#define ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES 1
+#else
+#define ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES 0
+#endif
+
+// ABSL_HAVE_STD_ANY
+//
+// Checks whether C++17 std::any is available by checking whether <any> exists.
+#ifdef ABSL_HAVE_STD_ANY
+#error "ABSL_HAVE_STD_ANY cannot be directly set."
+#endif
+
+#ifdef __has_include
+#if __has_include(<any>) && __cplusplus >= 201703L && \
+ ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES
+#define ABSL_HAVE_STD_ANY 1
+#endif
+#endif
+
+// ABSL_HAVE_STD_OPTIONAL
+//
+// Checks whether C++17 std::optional is available.
+#ifdef ABSL_HAVE_STD_OPTIONAL
+#error "ABSL_HAVE_STD_OPTIONAL cannot be directly set."
+#endif
+
+#ifdef __has_include
+#if __has_include(<optional>) && __cplusplus >= 201703L && \
+ ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES
+#define ABSL_HAVE_STD_OPTIONAL 1
+#endif
+#endif
+
+// ABSL_HAVE_STD_VARIANT
+//
+// Checks whether C++17 std::variant is available.
+#ifdef ABSL_HAVE_STD_VARIANT
+#error "ABSL_HAVE_STD_VARIANT cannot be directly set."
+#endif
+
+#ifdef __has_include
+#if __has_include(<variant>) && __cplusplus >= 201703L && \
+ ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES
+#define ABSL_HAVE_STD_VARIANT 1
+#endif
+#endif
+
+// ABSL_HAVE_STD_STRING_VIEW
+//
+// Checks whether C++17 std::string_view is available.
+#ifdef ABSL_HAVE_STD_STRING_VIEW
+#error "ABSL_HAVE_STD_STRING_VIEW cannot be directly set."
+#endif
+
+#ifdef __has_include
+#if __has_include(<string_view>) && __cplusplus >= 201703L
+#define ABSL_HAVE_STD_STRING_VIEW 1
+#endif
+// TODO(b/68770332) For now, using std::string_view is not practical.
+// This can be reversed when absl::string_view presents the std::string_view
+#undef ABSL_HAVE_STD_STRING_VIEW
+#endif
+
+// For MSVC, `__has_include` is supported in VS 2017 15.3, which is later than
+// the support for <optional>, <any>, <string_view>, <variant>. So we use
+// _MSC_VER to check whether we have VS 2017 RTM (when <optional>, <any>,
+// <string_view>, <variant> is implemented) or higher. Also, `__cplusplus` is
+// not correctly set by MSVC, so we use `_MSVC_LANG` to check the language
+// version.
+// TODO(user): fix tests before enabling aliasing for `std::any`.
+#if defined(_MSC_VER) && _MSC_VER >= 1910 && \
+ ((defined(_MSVC_LANG) && _MSVC_LANG > 201402) || __cplusplus > 201402)
+// #define ABSL_HAVE_STD_ANY 1
+#define ABSL_HAVE_STD_OPTIONAL 1
+#define ABSL_HAVE_STD_VARIANT 1
+#define ABSL_HAVE_STD_STRING_VIEW 1
+#endif
+
+// In debug mode, MSVC 2017's std::variant throws a EXCEPTION_ACCESS_VIOLATION
+// SEH exception from emplace for variant<SomeStruct> when constructing the
+// struct can throw. This defeats some of variant_test and
+// variant_exception_safety_test.
+// TODO(b/119200260) Fix when absl drops MSVC 2017 support
+#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_DEBUG)
+#define ABSL_INTERNAL_MSVC_2017_DBG_MODE
+#endif
+
+#endif // S2_THIRD_PARTY_ABSL_BASE_CONFIG_H_
--- /dev/null
+/*
+ * Copyright 2017 The Abseil Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* This file defines dynamic annotations for use with dynamic analysis
+ tool such as valgrind, PIN, etc.
+
+ Dynamic annotation is a source code annotation that affects
+ the generated code (that is, the annotation is not a comment).
+ Each such annotation is attached to a particular
+ instruction and/or to a particular object (address) in the program.
+
+ The annotations that should be used by users are macros in all upper-case
+ (e.g., ANNOTATE_THREAD_NAME).
+
+ Actual implementation of these macros may differ depending on the
+ dynamic analysis tool being used.
+
+ This file supports the following configurations:
+ - Dynamic Annotations enabled (with static thread-safety warnings disabled).
+ In this case, macros expand to functions implemented by Thread Sanitizer,
+ when building with TSan. When not provided an external implementation,
+ dynamic_annotations.cc provides no-op implementations.
+
+ - Static Clang thread-safety warnings enabled.
+ When building with a Clang compiler that supports thread-safety warnings,
+ a subset of annotations can be statically-checked at compile-time. We
+ expand these macros to static-inline functions that can be analyzed for
+ thread-safety, but afterwards elided when building the final binary.
+
+ - All annotations are disabled.
+ If neither Dynamic Annotations nor Clang thread-safety warnings are
+ enabled, then all annotation-macros expand to empty. */
+
+#ifndef S2_THIRD_PARTY_ABSL_BASE_DYNAMIC_ANNOTATIONS_H_
+#define S2_THIRD_PARTY_ABSL_BASE_DYNAMIC_ANNOTATIONS_H_
+
+#ifndef DYNAMIC_ANNOTATIONS_ENABLED
+# define DYNAMIC_ANNOTATIONS_ENABLED 0
+#endif
+
+#if DYNAMIC_ANNOTATIONS_ENABLED != 0
+
+ /* -------------------------------------------------------------
+ Annotations that suppress errors. It is usually better to express the
+ program's synchronization using the other annotations, but these can
+ be used when all else fails. */
+
+ /* Report that we may have a benign race at "pointer", with size
+ "sizeof(*(pointer))". "pointer" must be a non-void* pointer. Insert at the
+ point where "pointer" has been allocated, preferably close to the point
+ where the race happens. See also ANNOTATE_BENIGN_RACE_STATIC. */
+ #define ANNOTATE_BENIGN_RACE(pointer, description) \
+ AnnotateBenignRaceSized(__FILE__, __LINE__, pointer, \
+ sizeof(*(pointer)), description)
+
+ /* Same as ANNOTATE_BENIGN_RACE(address, description), but applies to
+ the memory range [address, address+size). */
+ #define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \
+ AnnotateBenignRaceSized(__FILE__, __LINE__, address, size, description)
+
+ /* Enable (enable!=0) or disable (enable==0) race detection for all threads.
+ This annotation could be useful if you want to skip expensive race analysis
+ during some period of program execution, e.g. during initialization. */
+ #define ANNOTATE_ENABLE_RACE_DETECTION(enable) \
+ AnnotateEnableRaceDetection(__FILE__, __LINE__, enable)
+
+ /* -------------------------------------------------------------
+ Annotations useful for debugging. */
+
+ /* Report the current thread name to a race detector. */
+ #define ANNOTATE_THREAD_NAME(name) \
+ AnnotateThreadName(__FILE__, __LINE__, name)
+
+ /* -------------------------------------------------------------
+ Annotations useful when implementing locks. They are not
+ normally needed by modules that merely use locks.
+ The "lock" argument is a pointer to the lock object. */
+
+ /* Report that a lock has been created at address "lock". */
+ #define ANNOTATE_RWLOCK_CREATE(lock) \
+ AnnotateRWLockCreate(__FILE__, __LINE__, lock)
+
+ /* Report that a linker initialized lock has been created at address "lock".
+ */
+#ifdef THREAD_SANITIZER
+ #define ANNOTATE_RWLOCK_CREATE_STATIC(lock) \
+ AnnotateRWLockCreateStatic(__FILE__, __LINE__, lock)
+#else
+ #define ANNOTATE_RWLOCK_CREATE_STATIC(lock) ANNOTATE_RWLOCK_CREATE(lock)
+#endif
+
+ /* Report that the lock at address "lock" is about to be destroyed. */
+ #define ANNOTATE_RWLOCK_DESTROY(lock) \
+ AnnotateRWLockDestroy(__FILE__, __LINE__, lock)
+
+ /* Report that the lock at address "lock" has been acquired.
+ is_w=1 for writer lock, is_w=0 for reader lock. */
+ #define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \
+ AnnotateRWLockAcquired(__FILE__, __LINE__, lock, is_w)
+
+ /* Report that the lock at address "lock" is about to be released. */
+ #define ANNOTATE_RWLOCK_RELEASED(lock, is_w) \
+ AnnotateRWLockReleased(__FILE__, __LINE__, lock, is_w)
+
+#else /* DYNAMIC_ANNOTATIONS_ENABLED == 0 */
+
+ #define ANNOTATE_RWLOCK_CREATE(lock) /* empty */
+ #define ANNOTATE_RWLOCK_CREATE_STATIC(lock) /* empty */
+ #define ANNOTATE_RWLOCK_DESTROY(lock) /* empty */
+ #define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) /* empty */
+ #define ANNOTATE_RWLOCK_RELEASED(lock, is_w) /* empty */
+ #define ANNOTATE_BENIGN_RACE(address, description) /* empty */
+ #define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) /* empty */
+ #define ANNOTATE_THREAD_NAME(name) /* empty */
+ #define ANNOTATE_ENABLE_RACE_DETECTION(enable) /* empty */
+
+#endif /* DYNAMIC_ANNOTATIONS_ENABLED */
+
+/* These annotations are also made available to LLVM's Memory Sanitizer */
+#if DYNAMIC_ANNOTATIONS_ENABLED == 1 || defined(MEMORY_SANITIZER)
+ #define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \
+ AnnotateMemoryIsInitialized(__FILE__, __LINE__, address, size)
+
+ #define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \
+ AnnotateMemoryIsUninitialized(__FILE__, __LINE__, address, size)
+#else
+ #define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) /* empty */
+ #define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) /* empty */
+#endif /* DYNAMIC_ANNOTATIONS_ENABLED || MEMORY_SANITIZER */
+
+#ifdef SWIG
+ #define ATTRIBUTE_IGNORE_READS_BEGIN /* empty */
+ #define ATTRIBUTE_IGNORE_READS_END /* empty */
+#else // SWIG
+/* TODO(user) -- Replace __CLANG_SUPPORT_DYN_ANNOTATION__ with the
+ appropriate feature ID. */
+#if defined(__clang__) && (!defined(SWIG)) \
+ && defined(__CLANG_SUPPORT_DYN_ANNOTATION__)
+
+ #if DYNAMIC_ANNOTATIONS_ENABLED == 0
+ #define ANNOTALYSIS_ENABLED
+ #endif
+
+ /* When running in opt-mode, GCC will issue a warning, if these attributes are
+ compiled. Only include them when compiling using Clang. */
+ #define ATTRIBUTE_IGNORE_READS_BEGIN \
+ __attribute((exclusive_lock_function("*")))
+ #define ATTRIBUTE_IGNORE_READS_END \
+ __attribute((unlock_function("*")))
+#else
+ #define ATTRIBUTE_IGNORE_READS_BEGIN /* empty */
+ #define ATTRIBUTE_IGNORE_READS_END /* empty */
+#endif /* defined(__clang__) && ... */
+#endif
+
+#if (DYNAMIC_ANNOTATIONS_ENABLED != 0) || defined(ANNOTALYSIS_ENABLED)
+ #define ANNOTATIONS_ENABLED
+#endif
+
+#if (DYNAMIC_ANNOTATIONS_ENABLED != 0)
+
+ /* Request the analysis tool to ignore all reads in the current thread
+ until ANNOTATE_IGNORE_READS_END is called.
+ Useful to ignore intentional racey reads, while still checking
+ other reads and all writes.
+ See also ANNOTATE_UNPROTECTED_READ. */
+ #define ANNOTATE_IGNORE_READS_BEGIN() \
+ AnnotateIgnoreReadsBegin(__FILE__, __LINE__)
+
+ /* Stop ignoring reads. */
+ #define ANNOTATE_IGNORE_READS_END() \
+ AnnotateIgnoreReadsEnd(__FILE__, __LINE__)
+
+ /* Similar to ANNOTATE_IGNORE_READS_BEGIN, but ignore writes instead. */
+ #define ANNOTATE_IGNORE_WRITES_BEGIN() \
+ AnnotateIgnoreWritesBegin(__FILE__, __LINE__)
+
+ /* Stop ignoring writes. */
+ #define ANNOTATE_IGNORE_WRITES_END() \
+ AnnotateIgnoreWritesEnd(__FILE__, __LINE__)
+
+/* Clang provides limited support for static thread-safety analysis
+ through a feature called Annotalysis. We configure macro-definitions
+ according to whether Annotalysis support is available. */
+#elif defined(ANNOTALYSIS_ENABLED)
+
+ #define ANNOTATE_IGNORE_READS_BEGIN() \
+ StaticAnnotateIgnoreReadsBegin(__FILE__, __LINE__)
+
+ #define ANNOTATE_IGNORE_READS_END() \
+ StaticAnnotateIgnoreReadsEnd(__FILE__, __LINE__)
+
+ #define ANNOTATE_IGNORE_WRITES_BEGIN() \
+ StaticAnnotateIgnoreWritesBegin(__FILE__, __LINE__)
+
+ #define ANNOTATE_IGNORE_WRITES_END() \
+ StaticAnnotateIgnoreWritesEnd(__FILE__, __LINE__)
+
+#else
+ #define ANNOTATE_IGNORE_READS_BEGIN() /* empty */
+ #define ANNOTATE_IGNORE_READS_END() /* empty */
+ #define ANNOTATE_IGNORE_WRITES_BEGIN() /* empty */
+ #define ANNOTATE_IGNORE_WRITES_END() /* empty */
+#endif
+
+/* Implement the ANNOTATE_IGNORE_READS_AND_WRITES_* annotations using the more
+ primitive annotations defined above. */
+#if defined(ANNOTATIONS_ENABLED)
+
+ /* Start ignoring all memory accesses (both reads and writes). */
+ #define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \
+ do { \
+ ANNOTATE_IGNORE_READS_BEGIN(); \
+ ANNOTATE_IGNORE_WRITES_BEGIN(); \
+ }while (0)
+
+ /* Stop ignoring both reads and writes. */
+ #define ANNOTATE_IGNORE_READS_AND_WRITES_END() \
+ do { \
+ ANNOTATE_IGNORE_WRITES_END(); \
+ ANNOTATE_IGNORE_READS_END(); \
+ }while (0)
+
+#else
+ #define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() /* empty */
+ #define ANNOTATE_IGNORE_READS_AND_WRITES_END() /* empty */
+#endif
+
+/* Use the macros above rather than using these functions directly. */
+#include <cstddef>
+#ifdef __cplusplus
+extern "C" {
+#endif
+void AnnotateRWLockCreate(const char *file, int line,
+ const volatile void *lock);
+void AnnotateRWLockCreateStatic(const char *file, int line,
+ const volatile void *lock);
+void AnnotateRWLockDestroy(const char *file, int line,
+ const volatile void *lock);
+void AnnotateRWLockAcquired(const char *file, int line,
+ const volatile void *lock, long is_w); /* NOLINT */
+void AnnotateRWLockReleased(const char *file, int line,
+ const volatile void *lock, long is_w); /* NOLINT */
+void AnnotateBenignRace(const char *file, int line,
+ const volatile void *address,
+ const char *description);
+void AnnotateBenignRaceSized(const char *file, int line,
+ const volatile void *address,
+ size_t size,
+ const char *description);
+void AnnotateThreadName(const char *file, int line,
+ const char *name);
+void AnnotateEnableRaceDetection(const char *file, int line, int enable);
+void AnnotateMemoryIsInitialized(const char *file, int line,
+ const volatile void *mem, size_t size);
+void AnnotateMemoryIsUninitialized(const char *file, int line,
+ const volatile void *mem, size_t size);
+
+/* Annotations expand to these functions, when Dynamic Annotations are enabled.
+ These functions are either implemented as no-op calls, if no Sanitizer is
+ attached, or provided with externally-linked implementations by a library
+ like ThreadSanitizer. */
+void AnnotateIgnoreReadsBegin(const char *file, int line)
+ ATTRIBUTE_IGNORE_READS_BEGIN;
+void AnnotateIgnoreReadsEnd(const char *file, int line)
+ ATTRIBUTE_IGNORE_READS_END;
+void AnnotateIgnoreWritesBegin(const char *file, int line);
+void AnnotateIgnoreWritesEnd(const char *file, int line);
+
+#if defined(ANNOTALYSIS_ENABLED)
+/* When Annotalysis is enabled without Dynamic Annotations, the use of
+ static-inline functions allows the annotations to be read at compile-time,
+ while still letting the compiler elide the functions from the final build.
+
+ TODO(user) -- The exclusive lock here ignores writes as well, but
+ allows IGNORE_READS_AND_WRITES to work properly. */
+static inline void StaticAnnotateIgnoreReadsBegin(const char *file, int line)
+ ATTRIBUTE_IGNORE_READS_BEGIN { (void)file; (void)line; }
+static inline void StaticAnnotateIgnoreReadsEnd(const char *file, int line)
+ ATTRIBUTE_IGNORE_READS_END { (void)file; (void)line; }
+static inline void StaticAnnotateIgnoreWritesBegin(
+ const char *file, int line) { (void)file; (void)line; }
+static inline void StaticAnnotateIgnoreWritesEnd(
+ const char *file, int line) { (void)file; (void)line; }
+#endif
+
+/* Return non-zero value if running under valgrind.
+
+ If "valgrind.h" is included into dynamic_annotations.cc,
+ the regular valgrind mechanism will be used.
+ See http://valgrind.org/docs/manual/manual-core-adv.html about
+ RUNNING_ON_VALGRIND and other valgrind "client requests".
+ The file "valgrind.h" may be obtained by doing
+ svn co svn://svn.valgrind.org/valgrind/trunk/include
+
+ If for some reason you can't use "valgrind.h" or want to fake valgrind,
+ there are two ways to make this function return non-zero:
+ - Use environment variable: export RUNNING_ON_VALGRIND=1
+ - Make your tool intercept the function RunningOnValgrind() and
+ change its return value.
+ */
+int RunningOnValgrind(void);
+
+/* ValgrindSlowdown returns:
+ * 1.0, if (RunningOnValgrind() == 0)
+ * 50.0, if (RunningOnValgrind() != 0 && getenv("VALGRIND_SLOWDOWN") == NULL)
+ * atof(getenv("VALGRIND_SLOWDOWN")) otherwise
+ This function can be used to scale timeout values:
+ EXAMPLE:
+ for (;;) {
+ DoExpensiveBackgroundTask();
+ SleepForSeconds(5 * ValgrindSlowdown());
+ }
+ */
+double ValgrindSlowdown(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* ANNOTATE_UNPROTECTED_READ is the preferred way to annotate racey reads.
+
+ Instead of doing
+ ANNOTATE_IGNORE_READS_BEGIN();
+ ... = x;
+ ANNOTATE_IGNORE_READS_END();
+ one can use
+ ... = ANNOTATE_UNPROTECTED_READ(x); */
+#if defined(__cplusplus) && defined(ANNOTATIONS_ENABLED)
+template <typename T>
+inline T ANNOTATE_UNPROTECTED_READ(const volatile T &x) { /* NOLINT */
+ ANNOTATE_IGNORE_READS_BEGIN();
+ T res = x;
+ ANNOTATE_IGNORE_READS_END();
+ return res;
+ }
+#else
+ #define ANNOTATE_UNPROTECTED_READ(x) (x)
+#endif
+
+#if DYNAMIC_ANNOTATIONS_ENABLED != 0 && defined(__cplusplus)
+ /* Apply ANNOTATE_BENIGN_RACE_SIZED to a static variable. */
+ #define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) \
+ namespace { \
+ class static_var ## _annotator { \
+ public: \
+ static_var ## _annotator() { \
+ ANNOTATE_BENIGN_RACE_SIZED(&static_var, \
+ sizeof(static_var), \
+ # static_var ": " description); \
+ } \
+ }; \
+ static static_var ## _annotator the ## static_var ## _annotator;\
+ } // namespace
+#else /* DYNAMIC_ANNOTATIONS_ENABLED == 0 */
+ #define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) /* empty */
+#endif /* DYNAMIC_ANNOTATIONS_ENABLED */
+
+#ifdef ADDRESS_SANITIZER
+/* Describe the current state of a contiguous container such as e.g.
+ * std::vector or std::string. For more details see
+ * sanitizer/common_interface_defs.h, which is provided by the compiler. */
+#include <sanitizer/common_interface_defs.h>
+#define ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) \
+ __sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid)
+#define ADDRESS_SANITIZER_REDZONE(name) \
+ struct { char x[8] __attribute__ ((aligned (8))); } name
+#else
+#define ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid)
+#define ADDRESS_SANITIZER_REDZONE(name)
+#endif // ADDRESS_SANITIZER
+
+/* Undefine the macros intended only in this file. */
+#undef ANNOTALYSIS_ENABLED
+#undef ANNOTATIONS_ENABLED
+#undef ATTRIBUTE_IGNORE_READS_BEGIN
+#undef ATTRIBUTE_IGNORE_READS_END
+
+#endif /* THIRD_PARTY_ABSL_BASE_DYNAMIC_ANNOTATIONS_H_ */
--- /dev/null
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef S2_THIRD_PARTY_ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_
+#define S2_THIRD_PARTY_ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_
+
+#include <atomic>
+#include <cassert>
+#include <cstdint>
+#include <utility>
+
+#ifdef _MSC_FULL_VER
+#define ABSL_HAVE_WORKING_ATOMIC_POINTER 0
+#else
+#define ABSL_HAVE_WORKING_ATOMIC_POINTER 1
+#endif
+
+namespace absl {
+namespace base_internal {
+
+template <typename T>
+class AtomicHook;
+
+// AtomicHook is a helper class, templatized on a raw function pointer type, for
+// implementing Abseil customization hooks. It is a callable object that
+// dispatches to the registered hook.
+//
+// A default constructed object performs a no-op (and returns a default
+// constructed object) if no hook has been registered.
+//
+// Hooks can be pre-registered via constant initialization, for example,
+// ABSL_CONST_INIT static AtomicHook<void(*)()> my_hook(DefaultAction);
+// and then changed at runtime via a call to Store().
+//
+// Reads and writes guarantee memory_order_acquire/memory_order_release
+// semantics.
+template <typename ReturnType, typename... Args>
+class AtomicHook<ReturnType (*)(Args...)> {
+ public:
+ using FnPtr = ReturnType (*)(Args...);
+
+ // Constructs an object that by default performs a no-op (and
+ // returns a default constructed object) when no hook as been registered.
+ constexpr AtomicHook() : AtomicHook(DummyFunction) {}
+
+ // Constructs an object that by default dispatches to/returns the
+ // pre-registered default_fn when no hook has been registered at runtime.
+#if ABSL_HAVE_WORKING_ATOMIC_POINTER
+ explicit constexpr AtomicHook(FnPtr default_fn)
+ : hook_(default_fn), default_fn_(default_fn) {}
+#else
+ explicit constexpr AtomicHook(FnPtr default_fn)
+ : hook_(kUninitialized), default_fn_(default_fn) {}
+#endif
+
+ // Stores the provided function pointer as the value for this hook.
+ //
+ // This is intended to be called once. Multiple calls are legal only if the
+ // same function pointer is provided for each call. The store is implemented
+ // as a memory_order_release operation, and read accesses are implemented as
+ // memory_order_acquire.
+ void Store(FnPtr fn) {
+ bool success = DoStore(fn);
+ static_cast<void>(success);
+ assert(success);
+ }
+
+ // Invokes the registered callback. If no callback has yet been registered, a
+ // default-constructed object of the appropriate type is returned instead.
+ template <typename... CallArgs>
+ ReturnType operator()(CallArgs&&... args) const {
+ return DoLoad()(std::forward<CallArgs>(args)...);
+ }
+
+ // Returns the registered callback, or nullptr if none has been registered.
+ // Useful if client code needs to conditionalize behavior based on whether a
+ // callback was registered.
+ //
+ // Note that atomic_hook.Load()() and atomic_hook() have different semantics:
+ // operator()() will perform a no-op if no callback was registered, while
+ // Load()() will dereference a null function pointer. Prefer operator()() to
+ // Load()() unless you must conditionalize behavior on whether a hook was
+ // registered.
+ FnPtr Load() const {
+ FnPtr ptr = DoLoad();
+ return (ptr == DummyFunction) ? nullptr : ptr;
+ }
+
+ private:
+ static ReturnType DummyFunction(Args...) {
+ return ReturnType();
+ }
+
+ // Current versions of MSVC (as of September 2017) have a broken
+ // implementation of std::atomic<T*>: Its constructor attempts to do the
+ // equivalent of a reinterpret_cast in a constexpr context, which is not
+ // allowed.
+ //
+ // This causes an issue when building with LLVM under Windows. To avoid this,
+ // we use a less-efficient, intptr_t-based implementation on Windows.
+ //
+ // TODO(b/65850802): revisit this if Windows resolves the std::atomic<T*>
+ // issue.
+#if ABSL_HAVE_WORKING_ATOMIC_POINTER
+ // Return the stored value, or DummyFunction if no value has been stored.
+ FnPtr DoLoad() const { return hook_.load(std::memory_order_acquire); }
+
+ // Store the given value. Returns false if a different value was already
+ // stored to this object.
+ bool DoStore(FnPtr fn) {
+ assert(fn);
+ FnPtr expected = default_fn_;
+ const bool store_succeeded = hook_.compare_exchange_strong(
+ expected, fn, std::memory_order_acq_rel, std::memory_order_acquire);
+ const bool same_value_already_stored = (expected == fn);
+ return store_succeeded || same_value_already_stored;
+ }
+
+ std::atomic<FnPtr> hook_;
+#else // !ABSL_HAVE_WORKING_ATOMIC_POINTER
+ // Use a sentinel value unlikely to be the address of an actual function.
+ static constexpr intptr_t kUninitialized = 0;
+
+ static_assert(sizeof(intptr_t) >= sizeof(FnPtr),
+ "intptr_t can't contain a function pointer");
+
+ FnPtr DoLoad() const {
+ const intptr_t value = hook_.load(std::memory_order_acquire);
+ if (value == kUninitialized) {
+ return default_fn_;
+ }
+ return reinterpret_cast<FnPtr>(value);
+ }
+
+ bool DoStore(FnPtr fn) {
+ assert(fn);
+ const auto value = reinterpret_cast<intptr_t>(fn);
+ intptr_t expected = kUninitialized;
+ const bool store_succeeded = hook_.compare_exchange_strong(
+ expected, value, std::memory_order_acq_rel, std::memory_order_acquire);
+ const bool same_value_already_stored = (expected == value);
+ return store_succeeded || same_value_already_stored;
+ }
+
+ std::atomic<intptr_t> hook_;
+#endif
+
+ const FnPtr default_fn_;
+};
+
+#undef ABSL_HAVE_WORKING_ATOMIC_POINTER
+
+} // namespace base_internal
+} // namespace absl
+
+#endif // S2_THIRD_PARTY_ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_
--- /dev/null
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef S2_THIRD_PARTY_ABSL_BASE_INTERNAL_IDENTITY_H_
+#define S2_THIRD_PARTY_ABSL_BASE_INTERNAL_IDENTITY_H_
+
+namespace absl {
+namespace internal {
+
+template <typename T>
+struct identity {
+ typedef T type;
+};
+
+template <typename T>
+using identity_t = typename identity<T>::type;
+
+} // namespace internal
+} // namespace absl
+
+#endif // S2_THIRD_PARTY_ABSL_BASE_INTERNAL_IDENTITY_H_
--- /dev/null
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef S2_THIRD_PARTY_ABSL_BASE_INTERNAL_INLINE_VARIABLE_EMULATION_H_
+#define S2_THIRD_PARTY_ABSL_BASE_INTERNAL_INLINE_VARIABLE_EMULATION_H_
+
+#include <type_traits>
+
+#include "s2/third_party/absl/base/internal/identity.h"
+
+// File:
+// This file define a macro that allows the creation of or emulation of C++17
+// inline variables based on whether or not the feature is supported.
+
+////////////////////////////////////////////////////////////////////////////////
+// Macro: ABSL_INTERNAL_INLINE_CONSTEXPR(type, name, init)
+//
+// Description:
+// Expands to the equivalent of an inline constexpr instance of the specified
+// `type` and `name`, initialized to the value `init`. If the compiler being
+// used is detected as supporting actual inline variables as a language
+// feature, then the macro expands to an actual inline variable definition.
+//
+// Requires:
+// `type` is a type that is usable in an extern variable declaration.
+//
+// Requires: `name` is a valid identifier
+//
+// Requires:
+// `init` is an expression that can be used in the following definition:
+// constexpr type name = init;
+//
+// Usage:
+//
+// // Equivalent to: `inline constexpr size_t variant_npos = -1;`
+// ABSL_INTERNAL_INLINE_CONSTEXPR(size_t, variant_npos, -1);
+//
+// Differences in implementation:
+// For a direct, language-level inline variable, decltype(name) will be the
+// type that was specified along with const qualification, whereas for
+// emulated inline variables, decltype(name) may be different (in practice
+// it will likely be a reference type).
+////////////////////////////////////////////////////////////////////////////////
+
+#ifdef __cpp_inline_variables
+
+// Clang's -Wmissing-variable-declarations option erroneously warned that
+// inline constexpr objects need to be pre-declared. This has now been fixed,
+// but we will need to support this workaround for people building with older
+// versions of clang.
+//
+// Bug: https://bugs.llvm.org/show_bug.cgi?id=35862
+//
+// Note:
+// identity_t is used here so that the const and name are in the
+// appropriate place for pointer types, reference types, function pointer
+// types, etc..
+#if defined(__clang__)
+#define ABSL_INTERNAL_EXTERN_DECL(type, name) \
+ extern const ::absl::internal::identity_t<type> name;
+#else // Otherwise, just define the macro to do nothing.
+#define ABSL_INTERNAL_EXTERN_DECL(type, name)
+#endif // defined(__clang__)
+
+// See above comment at top of file for details.
+#define ABSL_INTERNAL_INLINE_CONSTEXPR(type, name, init) \
+ ABSL_INTERNAL_EXTERN_DECL(type, name) \
+ inline constexpr ::absl::internal::identity_t<type> name = init
+
+#ifdef __clang__
+#undef ABSL_INTERNAL_INLINE_CONSTEXPR
+#define ABSL_INTERNAL_INLINE_CONSTEXPR(type, name, init) \
+ ABSL_INTERNAL_EXTERN_DECL(type, name) \
+ _Pragma("clang diagnostic push") \
+ _Pragma("clang diagnostic ignored \"-Wc++98-c++11-c++14-compat\"") \
+ inline constexpr ::absl::internal::identity_t<type> name = init \
+ _Pragma("clang diagnostic pop")
+#endif // __clang__
+
+#else
+
+// See above comment at top of file for details.
+//
+// Note:
+// identity_t is used here so that the const and name are in the
+// appropriate place for pointer types, reference types, function pointer
+// types, etc..
+#define ABSL_INTERNAL_INLINE_CONSTEXPR(var_type, name, init) \
+ template <class /*AbslInternalDummy*/ = void> \
+ struct AbslInternalInlineVariableHolder##name { \
+ static constexpr ::absl::internal::identity_t<var_type> kInstance = init; \
+ }; \
+ \
+ template <class AbslInternalDummy> \
+ constexpr ::absl::internal::identity_t<var_type> \
+ AbslInternalInlineVariableHolder##name<AbslInternalDummy>::kInstance; \
+ \
+ static constexpr const ::absl::internal::identity_t<var_type>& \
+ name = /* NOLINT */ \
+ AbslInternalInlineVariableHolder##name<>::kInstance; \
+ static_assert(sizeof(void (*)(decltype(name))) != 0, \
+ "Silence unused variable warnings.")
+
+#endif // __cpp_inline_variables
+
+#endif // S2_THIRD_PARTY_ABSL_BASE_INTERNAL_INLINE_VARIABLE_EMULATION_H_
--- /dev/null
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// absl::base_internal::Invoke(f, args...) is an implementation of
+// INVOKE(f, args...) from section [func.require] of the C++ standard.
+//
+// [func.require]
+// Define INVOKE (f, t1, t2, ..., tN) as follows:
+// 1. (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T
+// and t1 is an object of type T or a reference to an object of type T or a
+// reference to an object of a type derived from T;
+// 2. ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a
+// class T and t1 is not one of the types described in the previous item;
+// 3. t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is
+// an object of type T or a reference to an object of type T or a reference
+// to an object of a type derived from T;
+// 4. (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1
+// is not one of the types described in the previous item;
+// 5. f(t1, t2, ..., tN) in all other cases.
+//
+// The implementation is SFINAE-friendly: substitution failure within Invoke()
+// isn't an error.
+
+#ifndef S2_THIRD_PARTY_ABSL_BASE_INTERNAL_INVOKE_H_
+#define S2_THIRD_PARTY_ABSL_BASE_INTERNAL_INVOKE_H_
+
+#include <algorithm>
+#include <type_traits>
+#include <utility>
+
+// The following code is internal implementation detail. See the comment at the
+// top of this file for the API documentation.
+
+namespace absl {
+namespace base_internal {
+
+// The five classes below each implement one of the clauses from the definition
+// of INVOKE. The inner class template Accept<F, Args...> checks whether the
+// clause is applicable; static function template Invoke(f, args...) does the
+// invocation.
+//
+// By separating the clause selection logic from invocation we make sure that
+// Invoke() does exactly what the standard says.
+
+template <typename Derived>
+struct StrippedAccept {
+ template <typename... Args>
+ struct Accept : Derived::template AcceptImpl<typename std::remove_cv<
+ typename std::remove_reference<Args>::type>::type...> {};
+};
+
+// (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T
+// and t1 is an object of type T or a reference to an object of type T or a
+// reference to an object of a type derived from T.
+struct MemFunAndRef : StrippedAccept<MemFunAndRef> {
+ template <typename... Args>
+ struct AcceptImpl : std::false_type {};
+
+ template <typename R, typename C, typename... Params, typename Obj,
+ typename... Args>
+ struct AcceptImpl<R (C::*)(Params...), Obj, Args...>
+ : std::is_base_of<C, Obj> {};
+
+ template <typename R, typename C, typename... Params, typename Obj,
+ typename... Args>
+ struct AcceptImpl<R (C::*)(Params...) const, Obj, Args...>
+ : std::is_base_of<C, Obj> {};
+
+ template <typename MemFun, typename Obj, typename... Args>
+ static decltype((std::declval<Obj>().*
+ std::declval<MemFun>())(std::declval<Args>()...))
+ Invoke(MemFun&& mem_fun, Obj&& obj, Args&&... args) {
+ return (std::forward<Obj>(obj).*
+ std::forward<MemFun>(mem_fun))(std::forward<Args>(args)...);
+ }
+};
+
+// ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a
+// class T and t1 is not one of the types described in the previous item.
+struct MemFunAndPtr : StrippedAccept<MemFunAndPtr> {
+ template <typename... Args>
+ struct AcceptImpl : std::false_type {};
+
+ template <typename R, typename C, typename... Params, typename Ptr,
+ typename... Args>
+ struct AcceptImpl<R (C::*)(Params...), Ptr, Args...>
+ : std::integral_constant<bool, !std::is_base_of<C, Ptr>::value> {};
+
+ template <typename R, typename C, typename... Params, typename Ptr,
+ typename... Args>
+ struct AcceptImpl<R (C::*)(Params...) const, Ptr, Args...>
+ : std::integral_constant<bool, !std::is_base_of<C, Ptr>::value> {};
+
+ template <typename MemFun, typename Ptr, typename... Args>
+ static decltype(((*std::declval<Ptr>()).*
+ std::declval<MemFun>())(std::declval<Args>()...))
+ Invoke(MemFun&& mem_fun, Ptr&& ptr, Args&&... args) {
+ return ((*std::forward<Ptr>(ptr)).*
+ std::forward<MemFun>(mem_fun))(std::forward<Args>(args)...);
+ }
+};
+
+// t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is
+// an object of type T or a reference to an object of type T or a reference
+// to an object of a type derived from T.
+struct DataMemAndRef : StrippedAccept<DataMemAndRef> {
+ template <typename... Args>
+ struct AcceptImpl : std::false_type {};
+
+ template <typename R, typename C, typename Obj>
+ struct AcceptImpl<R C::*, Obj> : std::is_base_of<C, Obj> {};
+
+ template <typename DataMem, typename Ref>
+ static decltype(std::declval<Ref>().*std::declval<DataMem>()) Invoke(
+ DataMem&& data_mem, Ref&& ref) {
+ return std::forward<Ref>(ref).*std::forward<DataMem>(data_mem);
+ }
+};
+
+// (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1
+// is not one of the types described in the previous item.
+struct DataMemAndPtr : StrippedAccept<DataMemAndPtr> {
+ template <typename... Args>
+ struct AcceptImpl : std::false_type {};
+
+ template <typename R, typename C, typename Ptr>
+ struct AcceptImpl<R C::*, Ptr>
+ : std::integral_constant<bool, !std::is_base_of<C, Ptr>::value> {};
+
+ template <typename DataMem, typename Ptr>
+ static decltype((*std::declval<Ptr>()).*std::declval<DataMem>()) Invoke(
+ DataMem&& data_mem, Ptr&& ptr) {
+ return (*std::forward<Ptr>(ptr)).*std::forward<DataMem>(data_mem);
+ }
+};
+
+// f(t1, t2, ..., tN) in all other cases.
+struct Callable {
+ // Callable doesn't have Accept because it's the last clause that gets picked
+ // when none of the previous clauses are applicable.
+ template <typename F, typename... Args>
+ static decltype(std::declval<F>()(std::declval<Args>()...)) Invoke(
+ F&& f, Args&&... args) {
+ return std::forward<F>(f)(std::forward<Args>(args)...);
+ }
+};
+
+// Resolves to the first matching clause.
+template <typename... Args>
+struct Invoker {
+ typedef typename std::conditional<
+ MemFunAndRef::Accept<Args...>::value, MemFunAndRef,
+ typename std::conditional<
+ MemFunAndPtr::Accept<Args...>::value, MemFunAndPtr,
+ typename std::conditional<
+ DataMemAndRef::Accept<Args...>::value, DataMemAndRef,
+ typename std::conditional<DataMemAndPtr::Accept<Args...>::value,
+ DataMemAndPtr, Callable>::type>::type>::
+ type>::type type;
+};
+
+// The result type of Invoke<F, Args...>.
+template <typename F, typename... Args>
+using InvokeT = decltype(Invoker<F, Args...>::type::Invoke(
+ std::declval<F>(), std::declval<Args>()...));
+
+// Invoke(f, args...) is an implementation of INVOKE(f, args...) from section
+// [func.require] of the C++ standard.
+template <typename F, typename... Args>
+InvokeT<F, Args...> Invoke(F&& f, Args&&... args) {
+ return Invoker<F, Args...>::type::Invoke(std::forward<F>(f),
+ std::forward<Args>(args)...);
+}
+} // namespace base_internal
+} // namespace absl
+
+#endif // S2_THIRD_PARTY_ABSL_BASE_INTERNAL_INVOKE_H_
--- /dev/null
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Thread-safe logging routines that do not allocate any memory or
+// acquire any locks, and can therefore be used by low-level memory
+// allocation, synchronization, and signal-handling code.
+//
+// NOTE FOR GOOGLERS:
+//
+// IWYU pragma: private, include "base/raw_logging.h"
+
+#ifndef S2_THIRD_PARTY_ABSL_BASE_INTERNAL_RAW_LOGGING_H_
+#define S2_THIRD_PARTY_ABSL_BASE_INTERNAL_RAW_LOGGING_H_
+
+#include "s2/third_party/absl/base/attributes.h"
+#include "s2/third_party/absl/base/log_severity.h"
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/third_party/absl/base/port.h"
+
+// This is similar to LOG(severity) << format..., but
+// * it is to be used ONLY by low-level modules that can't use normal LOG()
+// * it is designed to be a low-level logger that does not allocate any
+// memory and does not need any locks, hence:
+// * it logs straight and ONLY to STDERR w/o buffering
+// * it uses an explicit printf-format and arguments list
+// * it will silently chop off really long message strings
+// Usage example:
+// ABSL_RAW_LOG(ERROR, "Failed foo with %i: %s", status, error);
+// This will print an almost standard log line like this to stderr only:
+// E0821 211317 file.cc:123] RAW: Failed foo with 22: bad_file
+
+#if !defined(STRIP_LOG) || STRIP_LOG == 0
+#define ABSL_RAW_LOG(severity, ...) \
+ do { \
+ constexpr const char* absl_raw_logging_internal_basename = \
+ ::absl::raw_logging_internal::Basename(__FILE__, \
+ sizeof(__FILE__) - 1); \
+ ::absl::raw_logging_internal::RawLog(ABSL_RAW_LOGGING_INTERNAL_##severity, \
+ absl_raw_logging_internal_basename, \
+ __LINE__, __VA_ARGS__); \
+ } while (0)
+#else
+#define ABSL_RAW_LOG(severity, ...) \
+ do { \
+ constexpr const char* absl_raw_logging_internal_basename = \
+ ::absl::raw_logging_internal::Basename(__FILE__, \
+ sizeof(__FILE__) - 1); \
+ if (ABSL_RAW_LOGGING_INTERNAL_##severity == ::absl::LogSeverity::kFatal) \
+ ::absl::raw_logging_internal::RawLog(::absl::LogSeverity::kFatal, \
+ absl_raw_logging_internal_basename, \
+ __LINE__, __VA_ARGS__); \
+ } while (0)
+#endif
+
+// Similar to CHECK(condition) << message, but for low-level modules:
+// we use only ABSL_RAW_LOG that does not allocate memory.
+// We do not want to provide args list here to encourage this usage:
+// if (!cond) ABSL_RAW_LOG(FATAL, "foo ...", hard_to_compute_args);
+// so that the args are not computed when not needed.
+#define ABSL_RAW_CHECK(condition, message) \
+ do { \
+ if (ABSL_PREDICT_FALSE(!(condition))) { \
+ ABSL_RAW_LOG(FATAL, "Check %s failed: %s", #condition, message); \
+ } \
+ } while (0)
+
+#ifndef NDEBUG
+
+#define RAW_DLOG(severity, ...) ABSL_RAW_LOG(severity, __VA_ARGS__)
+#define RAW_DCHECK(condition, message) ABSL_RAW_CHECK(condition, message)
+
+#else // NDEBUG
+
+#define RAW_DLOG(severity, ...) \
+ while (false) ABSL_RAW_LOG(severity, __VA_ARGS__)
+#define RAW_DCHECK(condition, message) \
+ while (false) ABSL_RAW_CHECK(condition, message)
+
+#endif // NDEBUG
+
+#define ABSL_RAW_LOGGING_INTERNAL_INFO ::absl::LogSeverity::kInfo
+#define ABSL_RAW_LOGGING_INTERNAL_WARNING ::absl::LogSeverity::kWarning
+#define ABSL_RAW_LOGGING_INTERNAL_ERROR ::absl::LogSeverity::kError
+#define ABSL_RAW_LOGGING_INTERNAL_FATAL ::absl::LogSeverity::kFatal
+#define ABSL_RAW_LOGGING_INTERNAL_DFATAL ::absl::kLogDebugFatal
+#define ABSL_RAW_LOGGING_INTERNAL_LEVEL(severity) \
+ ::absl::NormalizeLogSeverity(severity)
+
+namespace absl {
+namespace raw_logging_internal {
+
+// Helper function to implement ABSL_RAW_LOG
+// Logs format... at "severity" level, reporting it
+// as called from file:line.
+// This does not allocate memory or acquire locks.
+void RawLog(absl::LogSeverity severity, const char* file, int line,
+ const char* format, ...) ABSL_PRINTF_ATTRIBUTE(4, 5);
+void RawLog(int severity, const char* file, int line, const char* format, ...)
+ ABSL_PRINTF_ATTRIBUTE(4, 5);
+
+// Writes the provided buffer directly to stderr, in a safe, low-level manner.
+//
+// In POSIX this means calling write(), which is async-signal safe and does
+// not malloc. If the platform supports the SYS_write syscall, we invoke that
+// directly to side-step any libc interception.
+void SafeWriteToStderr(const char *s, size_t len);
+
+// compile-time function to get the "base" filename, that is, the part of
+// a filename after the last "/" or "\" path separator. The search starts at
+// the end of the string; the second parameter is the length of the string.
+constexpr const char* Basename(const char* fname, int offset) {
+ return offset == 0 || fname[offset - 1] == '/' || fname[offset - 1] == '\\'
+ ? fname + offset
+ : Basename(fname, offset - 1);
+}
+
+// For testing only.
+// Returns true if raw logging is fully supported. When it is not
+// fully supported, no messages will be emitted, but a log at FATAL
+// severity will cause an abort.
+//
+// TODO(user): Come up with a better name for this method.
+bool RawLoggingFullySupported();
+
+// Function type for a raw_logging customization hook for suppressing messages
+// by severity, and for writing custom prefixes on non-suppressed messages.
+//
+// The installed hook is called for every raw log invocation. The message will
+// be logged to stderr only if the hook returns true. FATAL errors will cause
+// the process to abort, even if writing to stderr is suppressed. The hook is
+// also provided with an output buffer, where it can write a custom log message
+// prefix.
+//
+// The raw_logging system does not allocate memory or grab locks. User-provided
+// hooks must avoid these operations, and must not throw exceptions.
+//
+// 'severity' is the severity level of the message being written.
+// 'file' and 'line' are the file and line number where the ABSL_RAW_LOG macro
+// was located.
+// 'buffer' and 'buf_size' are pointers to the buffer and buffer size. If the
+// hook writes a prefix, it must increment *buffer and decrement *buf_size
+// accordingly.
+using LogPrefixHook = bool (*)(absl::LogSeverity severity, const char* file,
+ int line, char** buffer, int* buf_size);
+
+// Function type for a raw_logging customization hook called to abort a process
+// when a FATAL message is logged. If the provided AbortHook() returns, the
+// logging system will call abort().
+//
+// 'file' and 'line' are the file and line number where the ABSL_RAW_LOG macro
+// was located.
+// The null-terminated logged message lives in the buffer between 'buf_start'
+// and 'buf_end'. 'prefix_end' points to the first non-prefix character of the
+// buffer (as written by the LogPrefixHook.)
+using AbortHook = void (*)(const char* file, int line, const char* buf_start,
+ const char* prefix_end, const char* buf_end);
+
+} // namespace raw_logging_internal
+} // namespace absl
+
+
+// TODO(b/62299050): These will be removed in post-launch cleanup
+#define RAW_LOG ABSL_RAW_LOG
+#define RAW_CHECK ABSL_RAW_CHECK
+
+namespace base_raw_logging {
+
+
+ABSL_DEPRECATED("Use absl::raw_logging_internal::RawLog instead.")
+void RawLog(absl::LogSeverity severity, const char* file, int line,
+ const char* format, ...) ABSL_PRINTF_ATTRIBUTE(4, 5);
+ABSL_DEPRECATED("Use absl::raw_logging_internal::RawLog instead.")
+void RawLog(int severity, const char* file, int line, const char* format, ...)
+ ABSL_PRINTF_ATTRIBUTE(4, 5);
+
+// Since raw_logging is internal in absl, the below functions and types
+// won't need to be shipped yet.
+
+using LogPrefixHook = absl::raw_logging_internal::LogPrefixHook;
+using AbortHook = absl::raw_logging_internal::AbortHook;
+
+// Registers hooks of the above types. Only a single hook of each type may be
+// registered. It is an error to call these functions multiple times with
+// different input arguments.
+//
+// These functions are safe to call at any point during initialization; they do
+// not block or malloc, and are async-signal safe.
+void RegisterLogPrefixHook(LogPrefixHook fn);
+void RegisterAbortHook(AbortHook fn);
+
+} // namespace base_raw_logging
+
+
+#endif // S2_THIRD_PARTY_ABSL_BASE_INTERNAL_RAW_LOGGING_H_
--- /dev/null
+//
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef S2_THIRD_PARTY_ABSL_BASE_INTERNAL_THROW_DELEGATE_H_
+#define S2_THIRD_PARTY_ABSL_BASE_INTERNAL_THROW_DELEGATE_H_
+
+#include <string>
+
+namespace absl {
+namespace base_internal {
+
+// Helper functions that allow throwing exceptions consistently from anywhere.
+// The main use case is for header-based libraries (eg templates), as they will
+// be built by many different targets with their own compiler options.
+// In particular, this will allow a safe way to throw exceptions even if the
+// caller is compiled with -fno-exceptions. This is intended for implementing
+// things like map<>::at(), which the standard documents as throwing an
+// exception on error.
+//
+// Using other techniques like #if tricks could lead to ODR violations.
+//
+// You shouldn't use it unless you're writing code that you know will be built
+// both with and without exceptions and you need to conform to an interface
+// that uses exceptions.
+
+[[noreturn]] void ThrowStdLogicError(const std::string& what_arg);
+[[noreturn]] void ThrowStdLogicError(const char* what_arg);
+[[noreturn]] void ThrowStdInvalidArgument(const std::string& what_arg);
+[[noreturn]] void ThrowStdInvalidArgument(const char* what_arg);
+[[noreturn]] void ThrowStdDomainError(const std::string& what_arg);
+[[noreturn]] void ThrowStdDomainError(const char* what_arg);
+[[noreturn]] void ThrowStdLengthError(const std::string& what_arg);
+[[noreturn]] void ThrowStdLengthError(const char* what_arg);
+[[noreturn]] void ThrowStdOutOfRange(const std::string& what_arg);
+[[noreturn]] void ThrowStdOutOfRange(const char* what_arg);
+[[noreturn]] void ThrowStdRuntimeError(const std::string& what_arg);
+[[noreturn]] void ThrowStdRuntimeError(const char* what_arg);
+[[noreturn]] void ThrowStdRangeError(const std::string& what_arg);
+[[noreturn]] void ThrowStdRangeError(const char* what_arg);
+[[noreturn]] void ThrowStdOverflowError(const std::string& what_arg);
+[[noreturn]] void ThrowStdOverflowError(const char* what_arg);
+[[noreturn]] void ThrowStdUnderflowError(const std::string& what_arg);
+[[noreturn]] void ThrowStdUnderflowError(const char* what_arg);
+
+[[noreturn]] void ThrowStdBadFunctionCall();
+[[noreturn]] void ThrowStdBadAlloc();
+
+// ThrowStdBadArrayNewLength() cannot be consistently supported because
+// std::bad_array_new_length is missing in libstdc++ until 4.9.0.
+// https://gcc.gnu.org/onlinedocs/gcc-4.8.3/libstdc++/api/a01379_source.html
+// https://gcc.gnu.org/onlinedocs/gcc-4.9.0/libstdc++/api/a01327_source.html
+// libcxx (as of 3.2) and msvc (as of 2015) both have it.
+// [[noreturn]] void ThrowStdBadArrayNewLength();
+
+} // namespace base_internal
+} // namespace absl
+
+#endif // S2_THIRD_PARTY_ABSL_BASE_INTERNAL_THROW_DELEGATE_H_
--- /dev/null
+//
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef S2_THIRD_PARTY_ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_
+#define S2_THIRD_PARTY_ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_
+
+#include <cstring>
+#include <cstdint>
+
+#include "s2/third_party/absl/base/attributes.h"
+
+// unaligned APIs
+
+// Portable handling of unaligned loads, stores, and copies.
+// On some platforms, like ARM, the copy functions can be more efficient
+// then a load and a store.
+//
+// It is possible to implement all of these these using constant-length memcpy
+// calls, which is portable and will usually be inlined into simple loads and
+// stores if the architecture supports it. However, such inlining usually
+// happens in a pass that's quite late in compilation, which means the resulting
+// loads and stores cannot participate in many other optimizations, leading to
+// overall worse code.
+// TODO(user): Reimplement these unaligned access APIs using memcpy() when
+// pretty early in the optimization pipeline. For x86-64 LLVM, this needs to be
+// solution works and is better than memcpy(). For ARM LLVM, the packed
+// attribute solution generates the same code as memcpy(). So we should just
+// to LLVM.
+
+// The unaligned API is C++ only. The declarations use C++ features
+// (namespaces, inline) which are absent or incompatible in C.
+#if defined(__cplusplus)
+
+#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) ||\
+ defined(MEMORY_SANITIZER)
+// Consider we have an unaligned load/store of 4 bytes from address 0x...05.
+// AddressSanitizer will treat it as a 3-byte access to the range 05:07 and
+// will miss a bug if 08 is the first unaddressable byte.
+// ThreadSanitizer will also treat this as a 3-byte access to 05:07 and will
+// miss a race between this access and some other accesses to 08.
+// MemorySanitizer will correctly propagate the shadow on unaligned stores
+// and correctly report bugs on unaligned loads, but it may not properly
+// update and report the origin of the uninitialized memory.
+// For all three tools, replacing an unaligned access with a tool-specific
+// callback solves the problem.
+
+// Make sure uint16_t/uint32_t/uint64_t are defined.
+#include <cstdint>
+
+extern "C" {
+uint16_t __sanitizer_unaligned_load16(const void *p);
+uint32_t __sanitizer_unaligned_load32(const void *p);
+uint64_t __sanitizer_unaligned_load64(const void *p);
+void __sanitizer_unaligned_store16(void *p, uint16_t v);
+void __sanitizer_unaligned_store32(void *p, uint32_t v);
+void __sanitizer_unaligned_store64(void *p, uint64_t v);
+} // extern "C"
+
+namespace absl {
+namespace base_internal {
+
+inline uint16_t UnalignedLoad16(const void *p) {
+ return __sanitizer_unaligned_load16(p);
+}
+
+inline uint32_t UnalignedLoad32(const void *p) {
+ return __sanitizer_unaligned_load32(p);
+}
+
+inline uint64_t UnalignedLoad64(const void *p) {
+ return __sanitizer_unaligned_load64(p);
+}
+
+inline void UnalignedStore16(void *p, uint16_t v) {
+ __sanitizer_unaligned_store16(p, v);
+}
+
+inline void UnalignedStore32(void *p, uint32_t v) {
+ __sanitizer_unaligned_store32(p, v);
+}
+
+inline void UnalignedStore64(void *p, uint64_t v) {
+ __sanitizer_unaligned_store64(p, v);
+}
+
+} // namespace base_internal
+} // namespace absl
+
+#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \
+ (absl::base_internal::UnalignedLoad16(_p))
+#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) \
+ (absl::base_internal::UnalignedLoad32(_p))
+#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) \
+ (absl::base_internal::UnalignedLoad64(_p))
+
+#define ABSL_INTERNAL_UNALIGNED_STORE16(_p, _val) \
+ (absl::base_internal::UnalignedStore16(_p, _val))
+#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \
+ (absl::base_internal::UnalignedStore32(_p, _val))
+#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \
+ (absl::base_internal::UnalignedStore64(_p, _val))
+
+#elif defined(UNDEFINED_BEHAVIOR_SANITIZER)
+
+namespace absl {
+namespace base_internal {
+
+inline uint16_t UnalignedLoad16(const void *p) {
+ uint16_t t;
+ memcpy(&t, p, sizeof t);
+ return t;
+}
+
+inline uint32_t UnalignedLoad32(const void *p) {
+ uint32_t t;
+ memcpy(&t, p, sizeof t);
+ return t;
+}
+
+inline uint64_t UnalignedLoad64(const void *p) {
+ uint64_t t;
+ memcpy(&t, p, sizeof t);
+ return t;
+}
+
+inline void UnalignedStore16(void *p, uint16_t v) { memcpy(p, &v, sizeof v); }
+
+inline void UnalignedStore32(void *p, uint32_t v) { memcpy(p, &v, sizeof v); }
+
+inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); }
+
+} // namespace base_internal
+} // namespace absl
+
+#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \
+ (absl::base_internal::UnalignedLoad16(_p))
+#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) \
+ (absl::base_internal::UnalignedLoad32(_p))
+#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) \
+ (absl::base_internal::UnalignedLoad64(_p))
+
+#define ABSL_INTERNAL_UNALIGNED_STORE16(_p, _val) \
+ (absl::base_internal::UnalignedStore16(_p, _val))
+#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \
+ (absl::base_internal::UnalignedStore32(_p, _val))
+#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \
+ (absl::base_internal::UnalignedStore64(_p, _val))
+
+#elif defined(__x86_64__) || defined(_M_X64) || defined(__i386) || \
+ defined(_M_IX86) || defined(__ppc__) || defined(__PPC__) || \
+ defined(__ppc64__) || defined(__PPC64__)
+
+// x86 and x86-64 can perform unaligned loads/stores directly;
+// modern PowerPC hardware can also do unaligned integer loads and stores;
+// but note: the FPU still sends unaligned loads and stores to a trap handler!
+
+#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \
+ (*reinterpret_cast<const uint16_t *>(_p))
+#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) \
+ (*reinterpret_cast<const uint32_t *>(_p))
+#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) \
+ (*reinterpret_cast<const uint64_t *>(_p))
+
+#define ABSL_INTERNAL_UNALIGNED_STORE16(_p, _val) \
+ (*reinterpret_cast<uint16_t *>(_p) = (_val))
+#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \
+ (*reinterpret_cast<uint32_t *>(_p) = (_val))
+#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \
+ (*reinterpret_cast<uint64_t *>(_p) = (_val))
+
+#elif defined(__arm__) && \
+ !defined(__ARM_ARCH_5__) && \
+ !defined(__ARM_ARCH_5T__) && \
+ !defined(__ARM_ARCH_5TE__) && \
+ !defined(__ARM_ARCH_5TEJ__) && \
+ !defined(__ARM_ARCH_6__) && \
+ !defined(__ARM_ARCH_6J__) && \
+ !defined(__ARM_ARCH_6K__) && \
+ !defined(__ARM_ARCH_6Z__) && \
+ !defined(__ARM_ARCH_6ZK__) && \
+ !defined(__ARM_ARCH_6T2__)
+
+
+// ARMv7 and newer support native unaligned accesses, but only of 16-bit
+// and 32-bit values (not 64-bit); older versions either raise a fatal signal,
+// do an unaligned read and rotate the words around a bit, or do the reads very
+// slowly (trip through kernel mode). There's no simple #define that says just
+// "ARMv7 or higher", so we have to filter away all ARMv5 and ARMv6
+// sub-architectures. Newer gcc (>= 4.6) set an __ARM_FEATURE_ALIGNED #define,
+// so in time, maybe we can move on to that.
+//
+// This is a mess, but there's not much we can do about it.
+//
+// To further complicate matters, only LDR instructions (single reads) are
+// allowed to be unaligned, not LDRD (two reads) or LDM (many reads). Unless we
+// explicitly tell the compiler that these accesses can be unaligned, it can and
+// will combine accesses. On armcc, the way to signal this is done by accessing
+// through the type (uint32_t __packed *), but GCC has no such attribute
+// (it ignores __attribute__((packed)) on individual variables). However,
+// we can tell it that a _struct_ is unaligned, which has the same effect,
+// so we do that.
+
+namespace absl {
+namespace base_internal {
+
+struct Unaligned16Struct {
+ uint16_t value;
+ uint8_t dummy; // To make the size non-power-of-two.
+} ABSL_ATTRIBUTE_PACKED;
+
+struct Unaligned32Struct {
+ uint32_t value;
+ uint8_t dummy; // To make the size non-power-of-two.
+} ABSL_ATTRIBUTE_PACKED;
+
+} // namespace base_internal
+} // namespace absl
+
+#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \
+ ((reinterpret_cast<const ::absl::base_internal::Unaligned16Struct *>(_p)) \
+ ->value)
+#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) \
+ ((reinterpret_cast<const ::absl::base_internal::Unaligned32Struct *>(_p)) \
+ ->value)
+
+#define ABSL_INTERNAL_UNALIGNED_STORE16(_p, _val) \
+ ((reinterpret_cast< ::absl::base_internal::Unaligned16Struct *>(_p)) \
+ ->value = (_val))
+#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \
+ ((reinterpret_cast< ::absl::base_internal::Unaligned32Struct *>(_p)) \
+ ->value = (_val))
+
+namespace absl {
+namespace base_internal {
+
+inline uint64_t UnalignedLoad64(const void *p) {
+ uint64_t t;
+ memcpy(&t, p, sizeof t);
+ return t;
+}
+
+inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); }
+
+} // namespace base_internal
+} // namespace absl
+
+#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) \
+ (absl::base_internal::UnalignedLoad64(_p))
+#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \
+ (absl::base_internal::UnalignedStore64(_p, _val))
+
+#else
+
+// ABSL_INTERNAL_NEED_ALIGNED_LOADS is defined when the underlying platform
+// doesn't support unaligned access.
+#define ABSL_INTERNAL_NEED_ALIGNED_LOADS
+
+// These functions are provided for architectures that don't support
+// unaligned loads and stores.
+
+namespace absl {
+namespace base_internal {
+
+inline uint16_t UnalignedLoad16(const void *p) {
+ uint16_t t;
+ memcpy(&t, p, sizeof t);
+ return t;
+}
+
+inline uint32_t UnalignedLoad32(const void *p) {
+ uint32_t t;
+ memcpy(&t, p, sizeof t);
+ return t;
+}
+
+inline uint64_t UnalignedLoad64(const void *p) {
+ uint64_t t;
+ memcpy(&t, p, sizeof t);
+ return t;
+}
+
+inline void UnalignedStore16(void *p, uint16_t v) { memcpy(p, &v, sizeof v); }
+
+inline void UnalignedStore32(void *p, uint32_t v) { memcpy(p, &v, sizeof v); }
+
+inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); }
+
+} // namespace base_internal
+} // namespace absl
+
+#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \
+ (absl::base_internal::UnalignedLoad16(_p))
+#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) \
+ (absl::base_internal::UnalignedLoad32(_p))
+#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) \
+ (absl::base_internal::UnalignedLoad64(_p))
+
+#define ABSL_INTERNAL_UNALIGNED_STORE16(_p, _val) \
+ (absl::base_internal::UnalignedStore16(_p, _val))
+#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \
+ (absl::base_internal::UnalignedStore32(_p, _val))
+#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \
+ (absl::base_internal::UnalignedStore64(_p, _val))
+
+#endif
+
+#endif // defined(__cplusplus), end of unaligned API
+
+#endif // S2_THIRD_PARTY_ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_
--- /dev/null
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//
+// NOTE FOR GOOGLERS:
+//
+// IWYU pragma: private, include "base/log_severity.h"
+
+#ifndef S2_THIRD_PARTY_ABSL_BASE_INTERNAL_LOG_SEVERITY_H_
+#define S2_THIRD_PARTY_ABSL_BASE_INTERNAL_LOG_SEVERITY_H_
+
+#include <array>
+
+#include "s2/third_party/absl/base/attributes.h"
+
+namespace absl {
+
+// Four severity levels are defined. Logging APIs should terminate the program
+// when a message is logged at severity `kFatal`; the other levels have no
+// special semantics.
+enum class LogSeverity : int {
+ kInfo = 0,
+ kWarning = 1,
+ kError = 2,
+ kFatal = 3,
+};
+
+// Returns an iterable of all standard `absl::LogSeverity` values, ordered from
+// least to most severe.
+constexpr std::array<absl::LogSeverity, 4> LogSeverities() {
+ return {{absl::LogSeverity::kInfo, absl::LogSeverity::kWarning,
+ absl::LogSeverity::kError, absl::LogSeverity::kFatal}};
+}
+
+// `absl::kLogDebugFatal` equals `absl::LogSeverity::kFatal` in debug builds
+// (i.e. when `NDEBUG` is not defined) and `absl::LogSeverity::kError`
+// otherwise. It is extern to prevent ODR violations when compilation units
+// with different build settings are linked together.
+ABSL_CONST_INIT extern const absl::LogSeverity kLogDebugFatal;
+
+// Returns the all-caps string representation (e.g. "INFO") of the specified
+// severity level if it is one of the normal levels and "UNKNOWN" otherwise.
+constexpr const char* LogSeverityName(absl::LogSeverity s) {
+ return s == absl::LogSeverity::kInfo
+ ? "INFO"
+ : s == absl::LogSeverity::kWarning
+ ? "WARNING"
+ : s == absl::LogSeverity::kError
+ ? "ERROR"
+ : s == absl::LogSeverity::kFatal ? "FATAL" : "UNKNOWN";
+}
+
+// Values less than `kInfo` normalize to `kInfo`; values greater than `kFatal`
+// normalize to `kError` (**NOT** `kFatal`).
+constexpr absl::LogSeverity NormalizeLogSeverity(absl::LogSeverity s) {
+ return s < absl::LogSeverity::kInfo
+ ? absl::LogSeverity::kInfo
+ : s > absl::LogSeverity::kFatal ? absl::LogSeverity::kError : s;
+}
+constexpr absl::LogSeverity NormalizeLogSeverity(int s) {
+ return NormalizeLogSeverity(static_cast<absl::LogSeverity>(s));
+}
+
+} // namespace absl
+
+#endif // S2_THIRD_PARTY_ABSL_BASE_INTERNAL_LOG_SEVERITY_H_
--- /dev/null
+//
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: macros.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines the set of language macros used within Abseil code.
+// For the set of macros used to determine supported compilers and platforms,
+// see absl/base/config.h instead.
+//
+// This code is compiled directly on many platforms, including client
+// platforms like Windows, Mac, and embedded systems. Before making
+// any changes here, make sure that you're not breaking any platforms.
+
+#ifndef S2_THIRD_PARTY_ABSL_BASE_MACROS_H_
+#define S2_THIRD_PARTY_ABSL_BASE_MACROS_H_
+
+#include <cassert>
+#include <cstddef>
+
+#include "s2/third_party/absl/base/port.h"
+
+// ABSL_ARRAYSIZE()
+//
+// Returns the number of elements in an array as a compile-time constant, which
+// can be used in defining new arrays. If you use this macro on a pointer by
+// mistake, you will get a compile-time error.
+#define ABSL_ARRAYSIZE(array) \
+ (sizeof(::absl::macros_internal::ArraySizeHelper(array)))
+
+namespace absl {
+namespace macros_internal {
+// Note: this internal template function declaration is used by ABSL_ARRAYSIZE.
+// The function doesn't need a definition, as we only use its type.
+template <typename T, size_t N>
+auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N];
+} // namespace macros_internal
+} // namespace absl
+
+// TODO(b/62370839): Replace arraysize() with ABSL_ARRAYSIZE().
+template <typename T, size_t N>
+auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N];
+
+#define arraysize(array) (sizeof(ArraySizeHelper(array)))
+
+// kLinkerInitialized
+//
+// An enum used only as a constructor argument to indicate that a variable has
+// static storage duration, and that the constructor should do nothing to its
+// state. Use of this macro indicates to the reader that it is legal to
+// declare a static instance of the class, provided the constructor is given
+// the absl::base_internal::kLinkerInitialized argument.
+//
+// Normally, it is unsafe to declare a static variable that has a constructor or
+// a destructor because invocation order is undefined. However, if the type can
+// be zero-initialized (which the loader does for static variables) into a valid
+// state and the type's destructor does not affect storage, then a constructor
+// for static initialization can be declared.
+//
+// Example:
+// // Declaration
+// explicit MyClass(absl::base_internal:LinkerInitialized x) {}
+//
+// // Invocation
+// static MyClass my_global(absl::base_internal::kLinkerInitialized);
+namespace absl {
+namespace base_internal {
+enum LinkerInitialized {
+ kLinkerInitialized = 0,
+ LINKER_INITIALIZED = 0,
+};
+} // namespace base_internal
+} // namespace absl
+
+namespace base {
+using absl::base_internal::LinkerInitialized;
+using absl::base_internal::LINKER_INITIALIZED;
+} // namespace base
+
+// ABSL_FALLTHROUGH_INTENDED
+//
+// Annotates implicit fall-through between switch labels, allowing a case to
+// indicate intentional fallthrough and turn off warnings about any lack of a
+// `break` statement. The ABSL_FALLTHROUGH_INTENDED macro should be followed by
+// a semicolon and can be used in most places where `break` can, provided that
+// no statements exist between it and the next switch label.
+//
+// Example:
+//
+// switch (x) {
+// case 40:
+// case 41:
+// if (truth_is_out_there) {
+// ++x;
+// ABSL_FALLTHROUGH_INTENDED; // Use instead of/along with annotations
+// // in comments
+// } else {
+// return x;
+// }
+// case 42:
+// ...
+//
+// Notes: when compiled with clang in C++11 mode, the ABSL_FALLTHROUGH_INTENDED
+// macro is expanded to the [[clang::fallthrough]] attribute, which is analysed
+// when performing switch labels fall-through diagnostic
+// (`-Wimplicit-fallthrough`). See clang documentation on language extensions
+// for details:
+// http://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough
+//
+// When used with unsupported compilers, the ABSL_FALLTHROUGH_INTENDED macro
+// has no effect on diagnostics. In any case this macro has no effect on runtime
+// behavior and performance of code.
+// TODO(b/62370839): Replace FALLTHROUGH_INTENDED with
+// ABSL_FALLTHROUGH_INTENDED.
+#if defined(__clang__) && defined(__has_warning)
+#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough")
+#define FALLTHROUGH_INTENDED [[clang::fallthrough]]
+#endif
+#elif defined(__GNUC__) && __GNUC__ >= 7
+#define FALLTHROUGH_INTENDED [[gnu::fallthrough]]
+#endif
+
+#ifndef FALLTHROUGH_INTENDED
+#define FALLTHROUGH_INTENDED do { } while (0)
+#endif
+#ifdef ABSL_FALLTHROUGH_INTENDED
+#error "ABSL_FALLTHROUGH_INTENDED should not be defined."
+#endif
+
+// TODO(user): Use c++17 standard [[fallthrough]] macro, when supported.
+#if defined(__clang__) && defined(__has_warning)
+#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough")
+#define ABSL_FALLTHROUGH_INTENDED [[clang::fallthrough]]
+#endif
+#elif defined(__GNUC__) && __GNUC__ >= 7
+#define ABSL_FALLTHROUGH_INTENDED [[gnu::fallthrough]]
+#endif
+
+#ifndef ABSL_FALLTHROUGH_INTENDED
+#define ABSL_FALLTHROUGH_INTENDED \
+ do { \
+ } while (0)
+#endif
+
+// ABSL_DEPRECATED()
+//
+// Marks a deprecated class, struct, enum, function, method and variable
+// declarations. The macro argument is used as a custom diagnostic message (e.g.
+// suggestion of a better alternative).
+//
+// Example:
+//
+// class ABSL_DEPRECATED("Use Bar instead") Foo {...};
+// ABSL_DEPRECATED("Use Baz instead") void Bar() {...}
+//
+// Every usage of a deprecated entity will trigger a warning when compiled with
+// clang's `-Wdeprecated-declarations` option. This option is turned off by
+// default, but the warnings will be reported by clang-tidy.
+#if defined(__clang__) && __cplusplus >= 201103L
+// #define ABSL_DEPRECATED(message) __attribute__((deprecated(message)))
+#define ABSL_DEPRECATED(message)
+#endif
+
+#ifndef ABSL_DEPRECATED
+#define ABSL_DEPRECATED(message)
+#endif
+
+// ABSL_BAD_CALL_IF()
+//
+// Used on a function overload to trap bad calls: any call that matches the
+// overload will cause a compile-time error. This macro uses a clang-specific
+// "enable_if" attribute, as described at
+// http://clang.llvm.org/docs/AttributeReference.html#enable-if
+//
+// Overloads which use this macro should be bracketed by
+// `#ifdef ABSL_BAD_CALL_IF`.
+//
+// Example:
+//
+// int isdigit(int c);
+// #ifdef ABSL_BAD_CALL_IF
+// int isdigit(int c)
+// ABSL_BAD_CALL_IF(c <= -1 || c > 255,
+// "'c' must have the value of an unsigned char or EOF");
+// #endif // ABSL_BAD_CALL_IF
+
+#if defined(__clang__)
+# if __has_attribute(enable_if)
+# define ABSL_BAD_CALL_IF(expr, msg) \
+ __attribute__((enable_if(expr, "Bad call trap"), unavailable(msg)))
+# endif
+#endif
+
+// ABSL_ASSERT()
+//
+// In C++11, `assert` can't be used portably within constexpr functions.
+// ABSL_ASSERT functions as a runtime assert but works in C++11 constexpr
+// functions. Example:
+//
+// constexpr double Divide(double a, double b) {
+// return ABSL_ASSERT(b != 0), a / b;
+// }
+//
+// This macro is inspired by
+// https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/
+#if defined(NDEBUG)
+#define ABSL_ASSERT(expr) (false ? (void)(expr) : (void)0)
+#else
+#define ABSL_ASSERT(expr) \
+ (ABSL_PREDICT_TRUE((expr)) ? (void)0 \
+ : [] { assert(false && #expr); }()) // NOLINT
+#endif
+
+#ifdef ABSL_HAVE_EXCEPTIONS
+#define ABSL_INTERNAL_TRY try
+#define ABSL_INTERNAL_CATCH_ANY catch (...)
+#define ABSL_INTERNAL_RETHROW do { throw; } while (false)
+#else // ABSL_HAVE_EXCEPTIONS
+#define ABSL_INTERNAL_TRY if (true)
+#define ABSL_INTERNAL_CATCH_ANY else if (false)
+#define ABSL_INTERNAL_RETHROW do {} while (false)
+#endif // ABSL_HAVE_EXCEPTIONS
+
+#endif // S2_THIRD_PARTY_ABSL_BASE_MACROS_H_
--- /dev/null
+//
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: optimization.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines portable macros for performance optimization.
+
+#ifndef S2_THIRD_PARTY_ABSL_BASE_OPTIMIZATION_H_
+#define S2_THIRD_PARTY_ABSL_BASE_OPTIMIZATION_H_
+
+#include "s2/third_party/absl/base/config.h"
+
+// ABSL_BLOCK_TAIL_CALL_OPTIMIZATION
+//
+// Instructs the compiler to avoid optimizing tail-call recursion. Use of this
+// macro is useful when you wish to preserve the existing function order within
+// a stack trace for logging, debugging, or profiling purposes.
+//
+// Example:
+//
+// int f() {
+// int result = g();
+// ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
+// return result;
+// }
+#if defined(__pnacl__)
+#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() if (volatile int x = 0) { (void)x; }
+#elif defined(__clang__)
+// Clang will not tail call given inline volatile assembly.
+#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() __asm__ __volatile__("")
+#elif defined(__GNUC__)
+// GCC will not tail call given inline volatile assembly.
+#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() __asm__ __volatile__("")
+#elif defined(_MSC_VER)
+#include <intrin.h>
+// The __nop() intrinsic blocks the optimisation.
+#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() __nop()
+#else
+#define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() if (volatile int x = 0) { (void)x; }
+#endif
+
+// ABSL_CACHELINE_SIZE
+//
+// Explicitly defines the size of the L1 cache for purposes of alignment.
+// Setting the cacheline size allows you to specify that certain objects be
+// aligned on a cacheline boundary with `ABSL_CACHELINE_ALIGNED` declarations.
+// (See below.)
+//
+// NOTE: this macro should be replaced with the following C++17 features, when
+// those are generally available:
+//
+// * `std::hardware_constructive_interference_size`
+// * `std::hardware_destructive_interference_size`
+//
+// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html
+// for more information.
+#if defined(__GNUC__) && !defined(SWIG)
+// Cache line alignment
+#if defined(__i386__) || defined(__x86_64__)
+#define ABSL_CACHELINE_SIZE 64
+#elif defined(__powerpc64__)
+#define ABSL_CACHELINE_SIZE 128
+#elif defined(__aarch64__)
+// We would need to read special register ctr_el0 to find out L1 dcache size.
+// This value is a good estimate based on a real aarch64 machine.
+#define ABSL_CACHELINE_SIZE 64
+#elif defined(__arm__)
+// Cache line sizes for ARM: These values are not strictly correct since
+// cache line sizes depend on implementations, not architectures. There
+// are even implementations with cache line sizes configurable at boot
+// time.
+#if defined(__ARM_ARCH_5T__)
+#define ABSL_CACHELINE_SIZE 32
+#elif defined(__ARM_ARCH_7A__)
+#define ABSL_CACHELINE_SIZE 64
+#endif
+#endif
+
+#ifndef ABSL_CACHELINE_SIZE
+// A reasonable default guess. Note that overestimates tend to waste more
+// space, while underestimates tend to waste more time.
+#define ABSL_CACHELINE_SIZE 64
+#endif
+
+// ABSL_CACHELINE_ALIGNED
+//
+// Indicates that the declared object be cache aligned using
+// `ABSL_CACHELINE_SIZE` (see above). Cacheline aligning objects allows you to
+// load a set of related objects in the L1 cache for performance improvements.
+// Cacheline aligning objects properly allows constructive memory sharing and
+// prevents destructive (or "false") memory sharing.
+//
+// NOTE: this macro should be replaced with usage of `alignas()` using
+// `std::hardware_constructive_interference_size` and/or
+// `std::hardware_destructive_interference_size` when available within C++17.
+//
+// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html
+// for more information.
+//
+// On some compilers, `ABSL_CACHELINE_ALIGNED` expands to an `__attribute__`
+// or `__declspec` attribute. For compilers where this is not known to work,
+// the macro expands to nothing.
+//
+// No further guarantees are made here. The result of applying the macro
+// to variables and types is always implementation-defined.
+//
+// WARNING: It is easy to use this attribute incorrectly, even to the point
+// of causing bugs that are difficult to diagnose, crash, etc. It does not
+// of itself guarantee that objects are aligned to a cache line.
+//
+// NOTE: Some compilers are picky about the locations of annotations such as
+// this attribute, so prefer to put it at the beginning of your declaration.
+// For example,
+//
+// ABSL_CACHELINE_ALIGNED static Foo* foo = ...
+//
+// class ABSL_CACHELINE_ALIGNED Bar { ...
+//
+// Recommendations:
+//
+// 1) Consult compiler documentation; this comment is not kept in sync as
+// toolchains evolve.
+// 2) Verify your use has the intended effect. This often requires inspecting
+// the generated machine code.
+// 3) Prefer applying this attribute to individual variables. Avoid
+// applying it to types. This tends to localize the effect.
+// 4) See go/cache-line-interference for further guidance.
+#define ABSL_CACHELINE_ALIGNED __attribute__((aligned(ABSL_CACHELINE_SIZE)))
+#elif defined(_MSC_VER)
+#define ABSL_CACHELINE_SIZE 64
+#define ABSL_CACHELINE_ALIGNED __declspec(align(ABSL_CACHELINE_SIZE))
+#else
+#define ABSL_CACHELINE_SIZE 64
+#define ABSL_CACHELINE_ALIGNED
+#endif
+
+// ABSL_PREDICT_TRUE, ABSL_PREDICT_FALSE
+//
+// Enables the compiler to prioritize compilation using static analysis for
+// likely paths within a boolean branch.
+//
+// Example:
+//
+// if (ABSL_PREDICT_TRUE(expression)) {
+// return result; // Faster if more likely
+// } else {
+// return 0;
+// }
+//
+// Compilers can use the information that a certain branch is not likely to be
+// taken (for instance, a CHECK failure) to optimize for the common case in
+// the absence of better information (ie. compiling gcc with `-fprofile-arcs`).
+#if (ABSL_HAVE_BUILTIN(__builtin_expect) || \
+ (defined(__GNUC__) && !defined(__clang__))) && \
+ !defined(SWIG)
+#define ABSL_PREDICT_FALSE(x) (__builtin_expect(x, 0))
+#define ABSL_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1))
+#else
+#define ABSL_PREDICT_FALSE(x) (x)
+#define ABSL_PREDICT_TRUE(x) (x)
+#endif
+
+#endif // S2_THIRD_PARTY_ABSL_BASE_OPTIMIZATION_H_
--- /dev/null
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: policy_checks.h
+// -----------------------------------------------------------------------------
+//
+// This header enforces a minimum set of policies at build time, such as the
+// supported compiler and library versions. Unsupported configurations are
+// reported with `#error`. This enforcement is best effort, so successfully
+// compiling this header does not guarantee a supported configuration.
+
+#ifndef S2_THIRD_PARTY_ABSL_BASE_POLICY_CHECKS_H_
+#define S2_THIRD_PARTY_ABSL_BASE_POLICY_CHECKS_H_
+
+// Included for the __GLIBC_PREREQ macro used below.
+#include <climits>
+
+// Included for the _STLPORT_VERSION macro used below.
+#if defined(__cplusplus)
+#include <cstddef>
+#endif
+
+// -----------------------------------------------------------------------------
+// Operating System Check
+// -----------------------------------------------------------------------------
+
+#if defined(__CYGWIN__)
+#error "Cygwin is not supported."
+#endif
+
+// -----------------------------------------------------------------------------
+// Compiler Check
+// -----------------------------------------------------------------------------
+
+// We support MSVC++ 14.0 update 2 and later.
+// This minimum will go up.
+#if defined(_MSC_FULL_VER) && _MSC_FULL_VER < 190023918 && !defined(__clang__)
+#error "This package requires Visual Studio 2015 Update 2 or higher."
+#endif
+
+// We support gcc 4.7 and later.
+// This minimum will go up.
+// Do not test clang. As of crosstool v18, clang identifies as gcc 4.2.
+#if defined(__GNUC__) && !defined(__clang__)
+#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7)
+#error "This package requires gcc 4.7 or higher."
+#endif
+#endif
+
+// We support Apple Xcode clang 4.2.1 (version 421.11.65) and later.
+// This corresponds to Apple Xcode version 4.5.
+// This minimum will go up.
+#if defined(__apple_build_version__) && __apple_build_version__ < 4211165
+#error "This package requires __apple_build_version__ of 4211165 or higher."
+#endif
+
+// -----------------------------------------------------------------------------
+// C++ Version Check
+// -----------------------------------------------------------------------------
+
+// Enforce C++11 as the minimum. Note that Visual Studio has not
+// advanced __cplusplus despite being good enough for our purposes, so
+// so we exempt it from the check.
+#if !defined(SWIG)
+#if defined(__cplusplus) && !defined(_MSC_VER)
+#if __cplusplus < 201103L
+#error "C++ versions less than C++11 are not supported."
+#endif
+#endif
+#endif
+
+// -----------------------------------------------------------------------------
+// Standard Library Check
+// -----------------------------------------------------------------------------
+
+// We have chosen glibc 2.12 as the minimum as it was tagged for release
+// in May, 2010 and includes some functionality used in Google software
+// (for instance pthread_setname_np):
+// https://sourceware.org/ml/libc-alpha/2010-05/msg00000.html
+#if defined(__GLIBC__) && defined(__GLIBC_PREREQ)
+#if !__GLIBC_PREREQ(2, 12)
+#error "Minimum required version of glibc is 2.12."
+#endif
+#endif
+
+#if defined(_STLPORT_VERSION)
+#error "STLPort is not supported."
+#endif
+
+// -----------------------------------------------------------------------------
+// `char` Size Check
+// -----------------------------------------------------------------------------
+
+// Abseil currently assumes CHAR_BIT == 8. If you would like to use Abseil on a
+// platform where this is not the case, please provide us with the details about
+// your platform so we can consider relaxing this requirement.
+#if CHAR_BIT != 8
+#error "Abseil assumes CHAR_BIT == 8."
+#endif
+
+// -----------------------------------------------------------------------------
+// `int` Size Check
+// -----------------------------------------------------------------------------
+
+// Abseil currently assumes that an int is 4 bytes. If you would like to use
+// Abseil on a platform where this is not the case, please provide us with the
+// details about your platform so we can consider relaxing this requirement.
+#if INT_MAX < 2147483647
+#error "Abseil assumes that int is at least 4 bytes. "
+#endif
+
+#endif // S2_THIRD_PARTY_ABSL_BASE_POLICY_CHECKS_H_
--- /dev/null
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// This files is a forwarding header for other headers containing various
+// portability macros and functions.
+// This file is used for both C and C++!
+// It also contains obsolete things that are pending cleanup but need to stay
+// in Abseil for now.
+
+#ifndef S2_THIRD_PARTY_ABSL_BASE_PORT_H_
+#define S2_THIRD_PARTY_ABSL_BASE_PORT_H_
+
+#include "s2/third_party/absl/base/attributes.h"
+#include "s2/third_party/absl/base/config.h"
+#include "s2/third_party/absl/base/optimization.h"
+
+#ifdef SWIG
+%include "third_party/absl/base/attributes.h"
+#endif
+
+// -----------------------------------------------------------------------------
+// Obsolete (to be removed)
+// -----------------------------------------------------------------------------
+
+// HAS_GLOBAL_STRING
+// Some platforms have a ::string class that is different from ::std::string
+// (although the interface is the same, of course). On other platforms,
+// ::string is the same as ::std::string.
+#if defined(__cplusplus) && !defined(SWIG)
+#include <string>
+#ifndef HAS_GLOBAL_STRING
+using std::basic_string;
+using std::string;
+// TODO(user): using std::wstring?
+#endif // HAS_GLOBAL_STRING
+#endif // SWIG, __cplusplus
+
+// NOTE: These live in Abseil purely as a short-term layering workaround to
+// resolve a dependency chain between util/hash/hash, absl/strings, and //base:
+// in order for //base to depend on absl/strings, the includes of hash need
+// to be in absl, not //base. string_view defines hashes.
+//
+// -----------------------------------------------------------------------------
+// HASH_NAMESPACE, HASH_NAMESPACE_DECLARATION_START/END
+// -----------------------------------------------------------------------------
+
+// Define the namespace for pre-C++11 functors for hash_map and hash_set.
+// This is not the namespace for C++11 functors (that namespace is "std").
+//
+// We used to require that the build tool or Makefile provide this definition.
+// Now we usually get it from testing target macros. If the testing target
+// macros are different from an external definition, you will get a build
+// error.
+//
+// TODO(user): always get HASH_NAMESPACE from testing target macros.
+
+#if defined(__GNUC__) && defined(GOOGLE_GLIBCXX_VERSION)
+// Crosstool v17 or later.
+#define HASH_NAMESPACE __gnu_cxx
+#elif defined(_MSC_VER)
+// MSVC.
+// http://msdn.microsoft.com/en-us/library/6x7w9f6z(v=vs.100).aspx
+#define HASH_NAMESPACE stdext
+#elif defined(__APPLE__)
+// Xcode.
+#define HASH_NAMESPACE __gnu_cxx
+#elif defined(__GNUC__)
+// Some other version of gcc.
+#define HASH_NAMESPACE __gnu_cxx
+#else
+// HASH_NAMESPACE defined externally.
+// TODO(user): make this an error. Do not use external value of
+// HASH_NAMESPACE.
+#endif
+
+#ifndef HASH_NAMESPACE
+// TODO(user): try to delete this.
+// I think gcc 2.95.3 was the last toolchain to use this.
+#define HASH_NAMESPACE_DECLARATION_START
+#define HASH_NAMESPACE_DECLARATION_END
+#else
+#define HASH_NAMESPACE_DECLARATION_START namespace HASH_NAMESPACE {
+#define HASH_NAMESPACE_DECLARATION_END }
+#endif
+
+#endif // S2_THIRD_PARTY_ABSL_BASE_PORT_H_
--- /dev/null
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: thread_annotations.h
+// -----------------------------------------------------------------------------
+//
+// This header file contains macro definitions for thread safety annotations
+// that allow developers to document the locking policies of multi-threaded
+// code. The annotations can also help program analysis tools to identify
+// potential thread safety issues.
+//
+// Note that our annotation names differ substantially from those in the Clang
+// documentation
+//
+// http://clang.llvm.org/docs/ThreadSafetyAnalysis.html
+//
+// E..g. we use EXCLUSIVE_LOCKS_REQUIRED where the external docs use REQUIRES.
+//
+// For detailed discussion on the design of these annotations, please
+// see the design doc at
+//
+// These annotations are implemented using compiler attributes. Using the macros
+// defined here instead of raw attributes allow for portability and future
+// compatibility.
+//
+// When referring to mutexes in the arguments of the attributes, you should
+// use variable names or more complex expressions (e.g. my_object->mutex_)
+// that evaluate to a concrete mutex object whenever possible. If the mutex
+// you want to refer to is not in scope, you may use a member pointer
+// (e.g. &MyClass::mutex_) to refer to a mutex in some (unknown) object.
+
+#ifndef S2_THIRD_PARTY_ABSL_BASE_THREAD_ANNOTATIONS_H_
+#define S2_THIRD_PARTY_ABSL_BASE_THREAD_ANNOTATIONS_H_
+
+#if defined(__clang__) && (!defined(SWIG))
+#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
+#else
+#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
+#endif
+
+// GUARDED_BY()
+//
+// Documents if a shared field or global variable needs to be protected by a
+// mutex. GUARDED_BY() allows the user to specify a particular mutex that
+// should be held when accessing the annotated variable.
+//
+// Although this annotation (and PT_GUARDED_BY, below) cannot be applied to
+// local variables, a local variable and its associated mutex can often be
+// combined into a small class or struct, thereby allowing the annotation.
+//
+// Example:
+//
+// class Foo {
+// Mutex mu_;
+// int p1_ GUARDED_BY(mu_);
+// ...
+// };
+#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
+
+// PT_GUARDED_BY()
+//
+// Documents if the memory location pointed to by a pointer should be guarded
+// by a mutex when dereferencing the pointer.
+//
+// Example:
+// class Foo {
+// Mutex mu_;
+// int *p1_ PT_GUARDED_BY(mu_);
+// ...
+// };
+//
+// Note that a pointer variable to a shared memory location could itself be a
+// shared variable.
+//
+// Example:
+//
+// // `q_`, guarded by `mu1_`, points to a shared memory location that is
+// // guarded by `mu2_`:
+// int *q_ GUARDED_BY(mu1_) PT_GUARDED_BY(mu2_);
+#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
+
+// ACQUIRED_AFTER() / ACQUIRED_BEFORE()
+//
+// Documents the acquisition order between locks that can be held
+// simultaneously by a thread. For any two locks that need to be annotated
+// to establish an acquisition order, only one of them needs the annotation.
+// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER
+// and ACQUIRED_BEFORE.)
+//
+// As with GUARDED_BY, this is only applicable to mutexes that are shared
+// fields or global variables.
+//
+// Example:
+//
+// Mutex m1_;
+// Mutex m2_ ACQUIRED_AFTER(m1_);
+#define ACQUIRED_AFTER(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
+
+#define ACQUIRED_BEFORE(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
+
+// EXCLUSIVE_LOCKS_REQUIRED() / SHARED_LOCKS_REQUIRED()
+//
+// Documents a function that expects a mutex to be held prior to entry.
+// The mutex is expected to be held both on entry to, and exit from, the
+// function.
+//
+// An exclusive lock allows read-write access to the guarded data member(s), and
+// only one thread can acquire a lock exclusively at any one time. A shared lock
+// allows read-only access, and any number of threads can acquire a shared lock
+// concurrently.
+//
+// Generally, non-const methods should be annotated with
+// EXCLUSIVE_LOCKS_REQUIRED, while const methods should be annotated with
+// SHARED_LOCKS_REQUIRED.
+//
+// Example:
+//
+// Mutex mu1, mu2;
+// int a GUARDED_BY(mu1);
+// int b GUARDED_BY(mu2);
+//
+// void foo() EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... }
+// void bar() const SHARED_LOCKS_REQUIRED(mu1, mu2) { ... }
+#define EXCLUSIVE_LOCKS_REQUIRED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
+
+#define SHARED_LOCKS_REQUIRED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
+
+// LOCKS_EXCLUDED()
+//
+// Documents the locks acquired in the body of the function. These locks
+// cannot be held when calling this function (as Abseil's `Mutex` locks are
+// non-reentrant).
+#define LOCKS_EXCLUDED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
+
+// LOCK_RETURNED()
+//
+// Documents a function that returns a mutex without acquiring it. For example,
+// a public getter method that returns a pointer to a private mutex should
+// be annotated with LOCK_RETURNED.
+#define LOCK_RETURNED(x) \
+ THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
+
+// LOCKABLE
+//
+// Documents if a class/type is a lockable type (such as the `Mutex` class).
+#define LOCKABLE \
+ THREAD_ANNOTATION_ATTRIBUTE__(lockable)
+
+// SCOPED_LOCKABLE
+//
+// Documents if a class does RAII locking (such as the `MutexLock` class).
+// The constructor should use `LOCK_FUNCTION()` to specify the mutex that is
+// acquired, and the destructor should use `UNLOCK_FUNCTION()` with no
+// arguments; the analysis will assume that the destructor unlocks whatever the
+// constructor locked.
+#define SCOPED_LOCKABLE \
+ THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
+
+// EXCLUSIVE_LOCK_FUNCTION()
+//
+// Documents functions that acquire a lock in the body of a function, and do
+// not release it.
+#define EXCLUSIVE_LOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
+
+// SHARED_LOCK_FUNCTION()
+//
+// Documents functions that acquire a shared (reader) lock in the body of a
+// function, and do not release it.
+#define SHARED_LOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
+
+// UNLOCK_FUNCTION()
+//
+// Documents functions that expect a lock to be held on entry to the function,
+// and release it in the body of the function.
+#define UNLOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
+
+// EXCLUSIVE_TRYLOCK_FUNCTION() / SHARED_TRYLOCK_FUNCTION()
+//
+// Documents functions that try to acquire a lock, and return success or failure
+// (or a non-boolean value that can be interpreted as a boolean).
+// The first argument should be `true` for functions that return `true` on
+// success, or `false` for functions that return `false` on success. The second
+// argument specifies the mutex that is locked on success. If unspecified, this
+// mutex is assumed to be `this`.
+#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
+
+#define SHARED_TRYLOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
+
+// ASSERT_EXCLUSIVE_LOCK() / ASSERT_SHARED_LOCK()
+//
+// Documents functions that dynamically check to see if a lock is held, and fail
+// if it is not held.
+#define ASSERT_EXCLUSIVE_LOCK(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__))
+
+#define ASSERT_SHARED_LOCK(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_lock(__VA_ARGS__))
+
+// NO_THREAD_SAFETY_ANALYSIS
+//
+// Turns off thread safety checking within the body of a particular function.
+// This annotation is used to mark functions that are known to be correct, but
+// the locking behavior is more complicated than the analyzer can handle.
+#define NO_THREAD_SAFETY_ANALYSIS \
+ THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
+
+//------------------------------------------------------------------------------
+// Tool-Supplied Annotations
+//------------------------------------------------------------------------------
+
+// TS_UNCHECKED should be placed around lock expressions that are not valid
+// C++ syntax, but which are present for documentation purposes. These
+// annotations will be ignored by the analysis.
+#define TS_UNCHECKED(x) ""
+
+// TS_FIXME is used to mark lock expressions that are not valid C++ syntax.
+// It is used by automated tools to mark and disable invalid expressions.
+// The annotation should either be fixed, or changed to TS_UNCHECKED.
+#define TS_FIXME(x) ""
+
+// Like NO_THREAD_SAFETY_ANALYSIS, this turns off checking within the body of
+// a particular function. However, this attribute is used to mark functions
+// that are incorrect and need to be fixed. It is used by automated tools to
+// avoid breaking the build when the analysis is updated.
+// Code owners are expected to eventually fix the routine.
+#define NO_THREAD_SAFETY_ANALYSIS_FIXME NO_THREAD_SAFETY_ANALYSIS
+
+// Similar to NO_THREAD_SAFETY_ANALYSIS_FIXME, this macro marks a GUARDED_BY
+// annotation that needs to be fixed, because it is producing thread safety
+// warning. It disables the GUARDED_BY.
+#define GUARDED_BY_FIXME(x)
+
+// Disables warnings for a single read operation. This can be used to avoid
+// warnings when it is known that the read is not actually involved in a race,
+// but the compiler cannot confirm that.
+#define TS_UNCHECKED_READ(x) thread_safety_analysis::ts_unchecked_read(x)
+
+
+namespace thread_safety_analysis {
+
+// Takes a reference to a guarded data member, and returns an unguarded
+// reference.
+template <typename T>
+inline const T& ts_unchecked_read(const T& v) NO_THREAD_SAFETY_ANALYSIS {
+ return v;
+}
+
+template <typename T>
+inline T& ts_unchecked_read(T& v) NO_THREAD_SAFETY_ANALYSIS {
+ return v;
+}
+
+} // namespace thread_safety_analysis
+
+#endif // S2_THIRD_PARTY_ABSL_BASE_THREAD_ANNOTATIONS_H_
--- /dev/null
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: fixed_array.h
+// -----------------------------------------------------------------------------
+//
+// A `FixedArray<T>` represents a non-resizable array of `T` where the length of
+// the array can be determined at run-time. It is a good replacement for
+// non-standard and deprecated uses of `alloca()` and variable length arrays
+// within the GCC extension. (See
+// https://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html).
+//
+// `FixedArray` allocates small arrays inline, keeping performance fast by
+// avoiding heap operations. It also helps reduce the chances of
+// accidentally overflowing your stack if large input is passed to
+// your function.
+
+#ifndef S2_THIRD_PARTY_ABSL_CONTAINER_FIXED_ARRAY_H_
+#define S2_THIRD_PARTY_ABSL_CONTAINER_FIXED_ARRAY_H_
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <cstddef>
+#include <initializer_list>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <new>
+#include <type_traits>
+
+#include "s2/third_party/absl/algorithm/algorithm.h"
+#include "s2/third_party/absl/base/dynamic_annotations.h"
+#include "s2/third_party/absl/base/internal/throw_delegate.h"
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/third_party/absl/base/optimization.h"
+#include "s2/third_party/absl/base/port.h"
+#include "s2/third_party/absl/container/internal/compressed_tuple.h"
+#include "s2/third_party/absl/memory/memory.h"
+
+namespace absl {
+
+constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1);
+
+// -----------------------------------------------------------------------------
+// FixedArray
+// -----------------------------------------------------------------------------
+//
+// A `FixedArray` provides a run-time fixed-size array, allocating a small array
+// inline for efficiency.
+//
+// Most users should not specify an `inline_elements` argument and let
+// `FixedArray` automatically determine the number of elements
+// to store inline based on `sizeof(T)`. If `inline_elements` is specified, the
+// `FixedArray` implementation will use inline storage for arrays with a
+// length <= `inline_elements`.
+//
+// Note that a `FixedArray` constructed with a `size_type` argument will
+// default-initialize its values by leaving trivially constructible types
+// uninitialized (e.g. int, int[4], double), and others default-constructed.
+// This matches the behavior of c-style arrays and `std::array`, but not
+// `std::vector`.
+//
+// Note that `FixedArray` does not provide a public allocator; if it requires a
+// heap allocation, it will do so with global `::operator new[]()` and
+// `::operator delete[]()`, even if T provides class-scope overrides for these
+// operators.
+template <typename T, size_t N = kFixedArrayUseDefault,
+ typename A = std::allocator<T>>
+class FixedArray {
+ static_assert(!std::is_array<T>::value || std::extent<T>::value > 0,
+ "Arrays with unknown bounds cannot be used with FixedArray.");
+
+ static constexpr size_t kInlineBytesDefault = 256;
+
+ using AllocatorTraits = std::allocator_traits<A>;
+ // std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17,
+ // but this seems to be mostly pedantic.
+ template <typename Iterator>
+ using EnableIfForwardIterator = absl::enable_if_t<std::is_convertible<
+ typename std::iterator_traits<Iterator>::iterator_category,
+ std::forward_iterator_tag>::value>;
+ static constexpr bool NoexceptCopyable() {
+ return std::is_nothrow_copy_constructible<StorageElement>::value &&
+ absl::allocator_is_nothrow<allocator_type>::value;
+ }
+ static constexpr bool NoexceptMovable() {
+ return std::is_nothrow_move_constructible<StorageElement>::value &&
+ absl::allocator_is_nothrow<allocator_type>::value;
+ }
+ static constexpr bool DefaultConstructorIsNonTrivial() {
+ return !absl::is_trivially_default_constructible<StorageElement>::value;
+ }
+
+ public:
+ using allocator_type = typename AllocatorTraits::allocator_type;
+ using value_type = typename allocator_type::value_type;
+ using pointer = typename allocator_type::pointer;
+ using const_pointer = typename allocator_type::const_pointer;
+ using reference = typename allocator_type::reference;
+ using const_reference = typename allocator_type::const_reference;
+ using size_type = typename allocator_type::size_type;
+ using difference_type = typename allocator_type::difference_type;
+ using iterator = pointer;
+ using const_iterator = const_pointer;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ static constexpr size_type inline_elements =
+ (N == kFixedArrayUseDefault ? kInlineBytesDefault / sizeof(value_type)
+ : static_cast<size_type>(N));
+
+ FixedArray(
+ const FixedArray& other,
+ const allocator_type& a = allocator_type()) noexcept(NoexceptCopyable())
+ : FixedArray(other.begin(), other.end(), a) {}
+
+ FixedArray(
+ FixedArray&& other,
+ const allocator_type& a = allocator_type()) noexcept(NoexceptMovable())
+ : FixedArray(std::make_move_iterator(other.begin()),
+ std::make_move_iterator(other.end()), a) {}
+
+ // Creates an array object that can store `n` elements.
+ // Note that trivially constructible elements will be uninitialized.
+ explicit FixedArray(size_type n, const allocator_type& a = allocator_type())
+ : storage_(n, a) {
+ if (DefaultConstructorIsNonTrivial()) {
+ memory_internal::ConstructRange(storage_.alloc(), storage_.begin(),
+ storage_.end());
+ }
+ }
+
+ // Creates an array initialized with `n` copies of `val`.
+ FixedArray(size_type n, const value_type& val,
+ const allocator_type& a = allocator_type())
+ : storage_(n, a) {
+ memory_internal::ConstructRange(storage_.alloc(), storage_.begin(),
+ storage_.end(), val);
+ }
+
+ // Creates an array initialized with the size and contents of `init_list`.
+ FixedArray(std::initializer_list<value_type> init_list,
+ const allocator_type& a = allocator_type())
+ : FixedArray(init_list.begin(), init_list.end(), a) {}
+
+ // Creates an array initialized with the elements from the input
+ // range. The array's size will always be `std::distance(first, last)`.
+ // REQUIRES: Iterator must be a forward_iterator or better.
+ // TODO(user) The same disambiguation in std::vector requires only
+ // InputIterator, not ForwardIterator. Investigate this restriction.
+ template <typename Iterator, EnableIfForwardIterator<Iterator>* = nullptr>
+ FixedArray(Iterator first, Iterator last,
+ const allocator_type& a = allocator_type())
+ : storage_(std::distance(first, last), a) {
+ memory_internal::CopyRange(storage_.alloc(), storage_.begin(), first, last);
+ }
+
+ ~FixedArray() noexcept {
+ for (auto* cur = storage_.begin(); cur != storage_.end(); ++cur) {
+ AllocatorTraits::destroy(storage_.alloc(), cur);
+ }
+ }
+
+ // Assignments are deleted because they break the invariant that the size of a
+ // `FixedArray` never changes.
+ void operator=(FixedArray&&) = delete;
+ void operator=(const FixedArray&) = delete;
+
+ // FixedArray::size()
+ //
+ // Returns the length of the fixed array.
+ size_type size() const { return storage_.size(); }
+
+ // FixedArray::max_size()
+ //
+ // Returns the largest possible value of `std::distance(begin(), end())` for a
+ // `FixedArray<T>`. This is equivalent to the most possible addressable bytes
+ // over the number of bytes taken by T.
+ constexpr size_type max_size() const {
+ return (std::numeric_limits<difference_type>::max)() / sizeof(value_type);
+ }
+
+ // FixedArray::empty()
+ //
+ // Returns whether or not the fixed array is empty.
+ bool empty() const { return size() == 0; }
+
+ // FixedArray::memsize()
+ //
+ // Returns the memory size of the fixed array in bytes.
+ size_t memsize() const { return size() * sizeof(value_type); }
+
+ // FixedArray::data()
+ //
+ // Returns a const T* pointer to elements of the `FixedArray`. This pointer
+ // can be used to access (but not modify) the contained elements.
+ const_pointer data() const { return AsValueType(storage_.begin()); }
+
+ // Overload of FixedArray::data() to return a T* pointer to elements of the
+ // fixed array. This pointer can be used to access and modify the contained
+ // elements.
+ pointer data() { return AsValueType(storage_.begin()); }
+
+ // FixedArray::operator[]
+ //
+ // Returns a reference the ith element of the fixed array.
+ // REQUIRES: 0 <= i < size()
+ reference operator[](size_type i) {
+ assert(i < size());
+ return data()[i];
+ }
+
+ // Overload of FixedArray::operator()[] to return a const reference to the
+ // ith element of the fixed array.
+ // REQUIRES: 0 <= i < size()
+ const_reference operator[](size_type i) const {
+ assert(i < size());
+ return data()[i];
+ }
+
+ // FixedArray::at
+ //
+ // Bounds-checked access. Returns a reference to the ith element of the
+ // fiexed array, or throws std::out_of_range
+ reference at(size_type i) {
+ if (ABSL_PREDICT_FALSE(i >= size())) {
+ base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check");
+ }
+ return data()[i];
+ }
+
+ // Overload of FixedArray::at() to return a const reference to the ith element
+ // of the fixed array.
+ const_reference at(size_type i) const {
+ if (ABSL_PREDICT_FALSE(i >= size())) {
+ base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check");
+ }
+ return data()[i];
+ }
+
+ // FixedArray::front()
+ //
+ // Returns a reference to the first element of the fixed array.
+ reference front() { return *begin(); }
+
+ // Overload of FixedArray::front() to return a reference to the first element
+ // of a fixed array of const values.
+ const_reference front() const { return *begin(); }
+
+ // FixedArray::back()
+ //
+ // Returns a reference to the last element of the fixed array.
+ reference back() { return *(end() - 1); }
+
+ // Overload of FixedArray::back() to return a reference to the last element
+ // of a fixed array of const values.
+ const_reference back() const { return *(end() - 1); }
+
+ // FixedArray::begin()
+ //
+ // Returns an iterator to the beginning of the fixed array.
+ iterator begin() { return data(); }
+
+ // Overload of FixedArray::begin() to return a const iterator to the
+ // beginning of the fixed array.
+ const_iterator begin() const { return data(); }
+
+ // FixedArray::cbegin()
+ //
+ // Returns a const iterator to the beginning of the fixed array.
+ const_iterator cbegin() const { return begin(); }
+
+ // FixedArray::end()
+ //
+ // Returns an iterator to the end of the fixed array.
+ iterator end() { return data() + size(); }
+
+ // Overload of FixedArray::end() to return a const iterator to the end of the
+ // fixed array.
+ const_iterator end() const { return data() + size(); }
+
+ // FixedArray::cend()
+ //
+ // Returns a const iterator to the end of the fixed array.
+ const_iterator cend() const { return end(); }
+
+ // FixedArray::rbegin()
+ //
+ // Returns a reverse iterator from the end of the fixed array.
+ reverse_iterator rbegin() { return reverse_iterator(end()); }
+
+ // Overload of FixedArray::rbegin() to return a const reverse iterator from
+ // the end of the fixed array.
+ const_reverse_iterator rbegin() const {
+ return const_reverse_iterator(end());
+ }
+
+ // FixedArray::crbegin()
+ //
+ // Returns a const reverse iterator from the end of the fixed array.
+ const_reverse_iterator crbegin() const { return rbegin(); }
+
+ // FixedArray::rend()
+ //
+ // Returns a reverse iterator from the beginning of the fixed array.
+ reverse_iterator rend() { return reverse_iterator(begin()); }
+
+ // Overload of FixedArray::rend() for returning a const reverse iterator
+ // from the beginning of the fixed array.
+ const_reverse_iterator rend() const {
+ return const_reverse_iterator(begin());
+ }
+
+ // FixedArray::crend()
+ //
+ // Returns a reverse iterator from the beginning of the fixed array.
+ const_reverse_iterator crend() const { return rend(); }
+
+ // FixedArray::fill()
+ //
+ // Assigns the given `value` to all elements in the fixed array.
+ void fill(const value_type& val) { std::fill(begin(), end(), val); }
+
+ // Relational operators. Equality operators are elementwise using
+ // `operator==`, while order operators order FixedArrays lexicographically.
+ friend bool operator==(const FixedArray& lhs, const FixedArray& rhs) {
+ return absl::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
+ }
+
+ friend bool operator!=(const FixedArray& lhs, const FixedArray& rhs) {
+ return !(lhs == rhs);
+ }
+
+ friend bool operator<(const FixedArray& lhs, const FixedArray& rhs) {
+ return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(),
+ rhs.end());
+ }
+
+ friend bool operator>(const FixedArray& lhs, const FixedArray& rhs) {
+ return rhs < lhs;
+ }
+
+ friend bool operator<=(const FixedArray& lhs, const FixedArray& rhs) {
+ return !(rhs < lhs);
+ }
+
+ friend bool operator>=(const FixedArray& lhs, const FixedArray& rhs) {
+ return !(lhs < rhs);
+ }
+
+ template <typename H>
+ friend H AbslHashValue(H h, const FixedArray& v) {
+ return H::combine(H::combine_contiguous(std::move(h), v.data(), v.size()),
+ v.size());
+ }
+
+ private:
+ // StorageElement
+ //
+ // For FixedArrays with a C-style-array value_type, StorageElement is a POD
+ // wrapper struct called StorageElementWrapper that holds the value_type
+ // instance inside. This is needed for construction and destruction of the
+ // entire array regardless of how many dimensions it has. For all other cases,
+ // StorageElement is just an alias of value_type.
+ //
+ // Maintainer's Note: The simpler solution would be to simply wrap value_type
+ // in a struct whether it's an array or not. That causes some paranoid
+ // diagnostics to misfire, believing that 'data()' returns a pointer to a
+ // single element, rather than the packed array that it really is.
+ // e.g.:
+ //
+ // FixedArray<char> buf(1);
+ // sprintf(buf.data(), "foo");
+ //
+ // error: call to int __builtin___sprintf_chk(etc...)
+ // will always overflow destination buffer [-Werror]
+ //
+ template <typename OuterT = value_type,
+ typename InnerT = absl::remove_extent_t<OuterT>,
+ size_t InnerN = std::extent<OuterT>::value>
+ struct StorageElementWrapper {
+ InnerT array[InnerN];
+ };
+
+ using StorageElement =
+ absl::conditional_t<std::is_array<value_type>::value,
+ StorageElementWrapper<value_type>, value_type>;
+ using StorageElementBuffer =
+ absl::aligned_storage_t<sizeof(StorageElement), alignof(StorageElement)>;
+
+ static pointer AsValueType(pointer ptr) { return ptr; }
+ static pointer AsValueType(StorageElementWrapper<value_type>* ptr) {
+ return std::addressof(ptr->array);
+ }
+
+ static_assert(sizeof(StorageElement) == sizeof(value_type), "");
+ static_assert(alignof(StorageElement) == alignof(value_type), "");
+
+ struct NonEmptyInlinedStorage {
+ StorageElement* data() {
+ return reinterpret_cast<StorageElement*>(inlined_storage_.data());
+ }
+
+#ifdef ADDRESS_SANITIZER
+ void* RedzoneBegin() { return &redzone_begin_; }
+ void* RedzoneEnd() { return &redzone_end_ + 1; }
+#endif // ADDRESS_SANITIZER
+
+ void AnnotateConstruct(size_type);
+ void AnnotateDestruct(size_type);
+
+ ADDRESS_SANITIZER_REDZONE(redzone_begin_);
+ std::array<StorageElementBuffer, inline_elements> inlined_storage_;
+ ADDRESS_SANITIZER_REDZONE(redzone_end_);
+ };
+
+ struct EmptyInlinedStorage {
+ StorageElement* data() { return nullptr; }
+ void AnnotateConstruct(size_type) {}
+ void AnnotateDestruct(size_type) {}
+ };
+
+ using InlinedStorage =
+ absl::conditional_t<inline_elements == 0, EmptyInlinedStorage,
+ NonEmptyInlinedStorage>;
+
+ // Storage
+ //
+ // An instance of Storage manages the inline and out-of-line memory for
+ // instances of FixedArray. This guarantees that even when construction of
+ // individual elements fails in the FixedArray constructor body, the
+ // destructor for Storage will still be called and out-of-line memory will be
+ // properly deallocated.
+ //
+ class Storage : public InlinedStorage {
+ public:
+ Storage(size_type n, const allocator_type& a)
+ : size_alloc_(n, a), data_(InitializeData()) {}
+
+ ~Storage() noexcept {
+ if (UsingInlinedStorage(size())) {
+ InlinedStorage::AnnotateDestruct(size());
+ } else {
+ AllocatorTraits::deallocate(alloc(), AsValueType(begin()), size());
+ }
+ }
+
+ size_type size() const { return size_alloc_.template get<0>(); }
+ StorageElement* begin() const { return data_; }
+ StorageElement* end() const { return begin() + size(); }
+ allocator_type& alloc() {
+ return size_alloc_.template get<1>();
+ }
+
+ private:
+ static bool UsingInlinedStorage(size_type n) {
+ return n <= inline_elements;
+ }
+
+ StorageElement* InitializeData() {
+ if (UsingInlinedStorage(size())) {
+ InlinedStorage::AnnotateConstruct(size());
+ return InlinedStorage::data();
+ } else {
+ return reinterpret_cast<StorageElement*>(
+ AllocatorTraits::allocate(alloc(), size()));
+ }
+ }
+
+ // `CompressedTuple` takes advantage of EBCO for stateless `allocator_type`s
+ container_internal::CompressedTuple<size_type, allocator_type> size_alloc_;
+ StorageElement* data_;
+ };
+
+ Storage storage_;
+};
+
+template <typename T, size_t N, typename A>
+constexpr size_t FixedArray<T, N, A>::kInlineBytesDefault;
+
+template <typename T, size_t N, typename A>
+constexpr typename FixedArray<T, N, A>::size_type
+ FixedArray<T, N, A>::inline_elements;
+
+template <typename T, size_t N, typename A>
+void FixedArray<T, N, A>::NonEmptyInlinedStorage::AnnotateConstruct(
+ typename FixedArray<T, N, A>::size_type n) {
+#ifdef ADDRESS_SANITIZER
+ if (!n) return;
+ ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), RedzoneEnd(), data() + n);
+ ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), data(), RedzoneBegin());
+#endif // ADDRESS_SANITIZER
+ static_cast<void>(n); // Mark used when not in asan mode
+}
+
+template <typename T, size_t N, typename A>
+void FixedArray<T, N, A>::NonEmptyInlinedStorage::AnnotateDestruct(
+ typename FixedArray<T, N, A>::size_type n) {
+#ifdef ADDRESS_SANITIZER
+ if (!n) return;
+ ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), data() + n, RedzoneEnd());
+ ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), RedzoneBegin(), data());
+#endif // ADDRESS_SANITIZER
+ static_cast<void>(n); // Mark used when not in asan mode
+}
+} // namespace absl
+
+using absl::FixedArray;
+
+#endif // S2_THIRD_PARTY_ABSL_CONTAINER_FIXED_ARRAY_H_
--- /dev/null
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: inlined_vector.h
+// -----------------------------------------------------------------------------
+//
+// This header file contains the declaration and definition of an "inlined
+// vector" which behaves in an equivalent fashion to a `std::vector`, except
+// that storage for small sequences of the vector are provided inline without
+// requiring any heap allocation.
+//
+// An `absl::InlinedVector<T, N>` specifies the default capacity `N` as one of
+// its template parameters. Instances where `size() <= N` hold contained
+// elements in inline space. Typically `N` is very small so that sequences that
+// are expected to be short do not require allocations.
+//
+// An `absl::InlinedVector` does not usually require a specific allocator. If
+// the inlined vector grows beyond its initial constraints, it will need to
+// allocate (as any normal `std::vector` would). This is usually performed with
+// the default allocator (defined as `std::allocator<T>`). Optionally, a custom
+// allocator type may be specified as `A` in `absl::InlinedVector<T, N, A>`.
+
+#ifndef S2_THIRD_PARTY_ABSL_CONTAINER_INLINED_VECTOR_H_
+#define S2_THIRD_PARTY_ABSL_CONTAINER_INLINED_VECTOR_H_
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdlib>
+#include <cstring>
+#include <initializer_list>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+#include "s2/third_party/absl/algorithm/algorithm.h"
+#include "s2/third_party/absl/base/internal/throw_delegate.h"
+#include "s2/third_party/absl/base/optimization.h"
+#include "s2/third_party/absl/base/port.h"
+#include "s2/third_party/absl/memory/memory.h"
+
+
+namespace absl {
+
+// -----------------------------------------------------------------------------
+// InlinedVector
+// -----------------------------------------------------------------------------
+//
+// An `absl::InlinedVector` is designed to be a drop-in replacement for
+// `std::vector` for use cases where the vector's size is sufficiently small
+// that it can be inlined. If the inlined vector does grow beyond its estimated
+// capacity, it will trigger an initial allocation on the heap, and will behave
+// as a `std:vector`. The API of the `absl::InlinedVector` within this file is
+// designed to cover the same API footprint as covered by `std::vector`.
+template <typename T, size_t N, typename A = std::allocator<T>>
+class InlinedVector {
+ static_assert(N > 0, "InlinedVector requires inline capacity greater than 0");
+ constexpr static typename A::size_type inlined_capacity() {
+ return static_cast<typename A::size_type>(N);
+ }
+
+ template <typename Iterator>
+ using DisableIfIntegral =
+ absl::enable_if_t<!std::is_integral<Iterator>::value>;
+
+ template <typename Iterator>
+ using EnableIfInputIterator = absl::enable_if_t<std::is_convertible<
+ typename std::iterator_traits<Iterator>::iterator_category,
+ std::input_iterator_tag>::value>;
+
+ template <typename Iterator>
+ using IteratorCategory =
+ typename std::iterator_traits<Iterator>::iterator_category;
+
+ using rvalue_reference = typename A::value_type&&;
+
+ public:
+ using allocator_type = A;
+ using value_type = typename allocator_type::value_type;
+ using pointer = typename allocator_type::pointer;
+ using const_pointer = typename allocator_type::const_pointer;
+ using reference = typename allocator_type::reference;
+ using const_reference = typename allocator_type::const_reference;
+ using size_type = typename allocator_type::size_type;
+ using difference_type = typename allocator_type::difference_type;
+ using iterator = pointer;
+ using const_iterator = const_pointer;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ // ---------------------------------------------------------------------------
+ // InlinedVector Constructors and Destructor
+ // ---------------------------------------------------------------------------
+
+ // Creates an empty inlined vector with a default initialized allocator.
+ InlinedVector() noexcept(noexcept(allocator_type()))
+ : allocator_and_tag_(allocator_type()) {}
+
+ // Creates an empty inlined vector with a specified allocator.
+ explicit InlinedVector(const allocator_type& alloc) noexcept
+ : allocator_and_tag_(alloc) {}
+
+ // Creates an inlined vector with `n` copies of `value_type()`.
+ explicit InlinedVector(size_type n,
+ const allocator_type& alloc = allocator_type())
+ : allocator_and_tag_(alloc) {
+ InitAssign(n);
+ }
+
+ // Creates an inlined vector with `n` copies of `v`.
+ InlinedVector(size_type n, const_reference v,
+ const allocator_type& alloc = allocator_type())
+ : allocator_and_tag_(alloc) {
+ InitAssign(n, v);
+ }
+
+ // Creates an inlined vector of copies of the values in `init_list`.
+ InlinedVector(std::initializer_list<value_type> init_list,
+ const allocator_type& alloc = allocator_type())
+ : allocator_and_tag_(alloc) {
+ AppendRange(init_list.begin(), init_list.end());
+ }
+
+ // Creates an inlined vector with elements constructed from the provided
+ // Iterator range [`first`, `last`).
+ //
+ // NOTE: The `enable_if` prevents ambiguous interpretation between a call to
+ // this constructor with two integral arguments and a call to the above
+ // `InlinedVector(size_type, const_reference)` constructor.
+ template <typename InputIterator, DisableIfIntegral<InputIterator>* = nullptr>
+ InlinedVector(InputIterator first, InputIterator last,
+ const allocator_type& alloc = allocator_type())
+ : allocator_and_tag_(alloc) {
+ AppendRange(first, last);
+ }
+
+ // Creates a copy of `other` using `other`'s allocator.
+ InlinedVector(const InlinedVector& other);
+
+ // Creates a copy of `other` but with a specified allocator.
+ InlinedVector(const InlinedVector& other, const allocator_type& alloc);
+
+ // Creates an inlined vector by moving in the contents of `other`.
+ //
+ // NOTE: This move constructor does not allocate and only moves the underlying
+ // objects, so its `noexcept` specification depends on whether moving the
+ // underlying objects can throw or not. We assume:
+ // a) move constructors should only throw due to allocation failure and
+ // b) if `value_type`'s move constructor allocates, it uses the same
+ // allocation function as the `InlinedVector`'s allocator, so the move
+ // constructor is non-throwing if the allocator is non-throwing or
+ // `value_type`'s move constructor is specified as `noexcept`.
+ InlinedVector(InlinedVector&& v) noexcept(
+ absl::allocator_is_nothrow<allocator_type>::value ||
+ std::is_nothrow_move_constructible<value_type>::value);
+
+ // Creates an inlined vector by moving in the contents of `other`.
+ //
+ // NOTE: This move constructor allocates and subsequently moves the underlying
+ // objects, so its `noexcept` specification depends on whether the allocation
+ // can throw and whether moving the underlying objects can throw. Based on the
+ // same assumptions as above, the `noexcept` specification is dominated by
+ // whether the allocation can throw regardless of whether `value_type`'s move
+ // constructor is specified as `noexcept`.
+ InlinedVector(InlinedVector&& v, const allocator_type& alloc) noexcept(
+ absl::allocator_is_nothrow<allocator_type>::value);
+
+ ~InlinedVector() { clear(); }
+
+ // ---------------------------------------------------------------------------
+ // InlinedVector Member Accessors
+ // ---------------------------------------------------------------------------
+
+ // `InlinedVector::empty()`
+ //
+ // Checks if the inlined vector has no elements.
+ bool empty() const noexcept { return !size(); }
+
+ // `InlinedVector::size()`
+ //
+ // Returns the number of elements in the inlined vector.
+ size_type size() const noexcept { return tag().size(); }
+
+ // `InlinedVector::max_size()`
+ //
+ // Returns the maximum number of elements the vector can hold.
+ size_type max_size() const noexcept {
+ // One bit of the size storage is used to indicate whether the inlined
+ // vector is allocated. As a result, the maximum size of the container that
+ // we can express is half of the max for `size_type`.
+ return (std::numeric_limits<size_type>::max)() / 2;
+ }
+
+ // `InlinedVector::capacity()`
+ //
+ // Returns the number of elements that can be stored in the inlined vector
+ // without requiring a reallocation of underlying memory.
+ //
+ // NOTE: For most inlined vectors, `capacity()` should equal
+ // `inlined_capacity()`. For inlined vectors which exceed this capacity, they
+ // will no longer be inlined and `capacity()` will equal its capacity on the
+ // allocated heap.
+ size_type capacity() const noexcept {
+ return allocated() ? allocation().capacity() : inlined_capacity();
+ }
+
+ // `InlinedVector::data()`
+ //
+ // Returns a `pointer` to elements of the inlined vector. This pointer can be
+ // used to access and modify the contained elements.
+ // Only results within the range [`0`, `size()`) are defined.
+ pointer data() noexcept {
+ return allocated() ? allocated_space() : inlined_space();
+ }
+
+ // Overload of `InlinedVector::data()` to return a `const_pointer` to elements
+ // of the inlined vector. This pointer can be used to access (but not modify)
+ // the contained elements.
+ const_pointer data() const noexcept {
+ return allocated() ? allocated_space() : inlined_space();
+ }
+
+ // `InlinedVector::operator[]()`
+ //
+ // Returns a `reference` to the `i`th element of the inlined vector using the
+ // array operator.
+ reference operator[](size_type i) {
+ assert(i < size());
+ return data()[i];
+ }
+
+ // Overload of `InlinedVector::operator[]()` to return a `const_reference` to
+ // the `i`th element of the inlined vector.
+ const_reference operator[](size_type i) const {
+ assert(i < size());
+ return data()[i];
+ }
+
+ // `InlinedVector::at()`
+ //
+ // Returns a `reference` to the `i`th element of the inlined vector.
+ reference at(size_type i) {
+ if (ABSL_PREDICT_FALSE(i >= size())) {
+ base_internal::ThrowStdOutOfRange(
+ "InlinedVector::at() failed bounds check");
+ }
+ return data()[i];
+ }
+
+ // Overload of `InlinedVector::at()` to return a `const_reference` to the
+ // `i`th element of the inlined vector.
+ const_reference at(size_type i) const {
+ if (ABSL_PREDICT_FALSE(i >= size())) {
+ base_internal::ThrowStdOutOfRange(
+ "InlinedVector::at() failed bounds check");
+ }
+ return data()[i];
+ }
+
+ // `InlinedVector::front()`
+ //
+ // Returns a `reference` to the first element of the inlined vector.
+ reference front() {
+ assert(!empty());
+ return at(0);
+ }
+
+ // Overload of `InlinedVector::front()` returns a `const_reference` to the
+ // first element of the inlined vector.
+ const_reference front() const {
+ assert(!empty());
+ return at(0);
+ }
+
+ // `InlinedVector::back()`
+ //
+ // Returns a `reference` to the last element of the inlined vector.
+ reference back() {
+ assert(!empty());
+ return at(size() - 1);
+ }
+
+ // Overload of `InlinedVector::back()` to return a `const_reference` to the
+ // last element of the inlined vector.
+ const_reference back() const {
+ assert(!empty());
+ return at(size() - 1);
+ }
+
+ // `InlinedVector::begin()`
+ //
+ // Returns an `iterator` to the beginning of the inlined vector.
+ iterator begin() noexcept { return data(); }
+
+ // Overload of `InlinedVector::begin()` to return a `const_iterator` to
+ // the beginning of the inlined vector.
+ const_iterator begin() const noexcept { return data(); }
+
+ // `InlinedVector::end()`
+ //
+ // Returns an `iterator` to the end of the inlined vector.
+ iterator end() noexcept { return data() + size(); }
+
+ // Overload of `InlinedVector::end()` to return a `const_iterator` to the
+ // end of the inlined vector.
+ const_iterator end() const noexcept { return data() + size(); }
+
+ // `InlinedVector::cbegin()`
+ //
+ // Returns a `const_iterator` to the beginning of the inlined vector.
+ const_iterator cbegin() const noexcept { return begin(); }
+
+ // `InlinedVector::cend()`
+ //
+ // Returns a `const_iterator` to the end of the inlined vector.
+ const_iterator cend() const noexcept { return end(); }
+
+ // `InlinedVector::rbegin()`
+ //
+ // Returns a `reverse_iterator` from the end of the inlined vector.
+ reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
+
+ // Overload of `InlinedVector::rbegin()` to return a
+ // `const_reverse_iterator` from the end of the inlined vector.
+ const_reverse_iterator rbegin() const noexcept {
+ return const_reverse_iterator(end());
+ }
+
+ // `InlinedVector::rend()`
+ //
+ // Returns a `reverse_iterator` from the beginning of the inlined vector.
+ reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
+
+ // Overload of `InlinedVector::rend()` to return a `const_reverse_iterator`
+ // from the beginning of the inlined vector.
+ const_reverse_iterator rend() const noexcept {
+ return const_reverse_iterator(begin());
+ }
+
+ // `InlinedVector::crbegin()`
+ //
+ // Returns a `const_reverse_iterator` from the end of the inlined vector.
+ const_reverse_iterator crbegin() const noexcept { return rbegin(); }
+
+ // `InlinedVector::crend()`
+ //
+ // Returns a `const_reverse_iterator` from the beginning of the inlined
+ // vector.
+ const_reverse_iterator crend() const noexcept { return rend(); }
+
+ // `InlinedVector::get_allocator()`
+ //
+ // Returns a copy of the allocator of the inlined vector.
+ allocator_type get_allocator() const { return allocator(); }
+
+
+ // ---------------------------------------------------------------------------
+ // InlinedVector Member Mutators
+ // ---------------------------------------------------------------------------
+
+ // `InlinedVector::operator=()`
+ //
+ // Replaces the contents of the inlined vector with copies of the elements in
+ // the provided `std::initializer_list`.
+ InlinedVector& operator=(std::initializer_list<value_type> init_list) {
+ AssignRange(init_list.begin(), init_list.end());
+ return *this;
+ }
+
+ // Overload of `InlinedVector::operator=()` to replace the contents of the
+ // inlined vector with the contents of `other`.
+ InlinedVector& operator=(const InlinedVector& other) {
+ if (ABSL_PREDICT_FALSE(this == &other)) return *this;
+
+ // Optimized to avoid reallocation.
+ // Prefer reassignment to copy construction for elements.
+ if (size() < other.size()) { // grow
+ reserve(other.size());
+ std::copy(other.begin(), other.begin() + size(), begin());
+ std::copy(other.begin() + size(), other.end(), std::back_inserter(*this));
+ } else { // maybe shrink
+ erase(begin() + other.size(), end());
+ std::copy(other.begin(), other.end(), begin());
+ }
+ return *this;
+ }
+
+ // Overload of `InlinedVector::operator=()` to replace the contents of the
+ // inlined vector with the contents of `other`.
+ //
+ // NOTE: As a result of calling this overload, `other` may be empty or it's
+ // contents may be left in a moved-from state.
+ InlinedVector& operator=(InlinedVector&& other) {
+ if (ABSL_PREDICT_FALSE(this == &other)) return *this;
+
+ if (other.allocated()) {
+ clear();
+ tag().set_allocated_size(other.size());
+ init_allocation(other.allocation());
+ other.tag() = Tag();
+ } else {
+ if (allocated()) clear();
+ // Both are inlined now.
+ if (size() < other.size()) {
+ auto mid = std::make_move_iterator(other.begin() + size());
+ std::copy(std::make_move_iterator(other.begin()), mid, begin());
+ UninitializedCopy(mid, std::make_move_iterator(other.end()), end());
+ } else {
+ auto new_end = std::copy(std::make_move_iterator(other.begin()),
+ std::make_move_iterator(other.end()), begin());
+ Destroy(new_end, end());
+ }
+ tag().set_inline_size(other.size());
+ }
+ return *this;
+ }
+
+ // `InlinedVector::assign()`
+ //
+ // Replaces the contents of the inlined vector with `n` copies of `v`.
+ void assign(size_type n, const_reference v) {
+ if (n <= size()) { // Possibly shrink
+ std::fill_n(begin(), n, v);
+ erase(begin() + n, end());
+ return;
+ }
+ // Grow
+ reserve(n);
+ std::fill_n(begin(), size(), v);
+ if (allocated()) {
+ UninitializedFill(allocated_space() + size(), allocated_space() + n, v);
+ tag().set_allocated_size(n);
+ } else {
+ UninitializedFill(inlined_space() + size(), inlined_space() + n, v);
+ tag().set_inline_size(n);
+ }
+ }
+
+ // Overload of `InlinedVector::assign()` to replace the contents of the
+ // inlined vector with copies of the values in the provided
+ // `std::initializer_list`.
+ void assign(std::initializer_list<value_type> init_list) {
+ AssignRange(init_list.begin(), init_list.end());
+ }
+
+ // Overload of `InlinedVector::assign()` to replace the contents of the
+ // inlined vector with values constructed from the range [`first`, `last`).
+ template <typename InputIterator, DisableIfIntegral<InputIterator>* = nullptr>
+ void assign(InputIterator first, InputIterator last) {
+ AssignRange(first, last);
+ }
+
+ // `InlinedVector::resize()`
+ //
+ // Resizes the inlined vector to contain `n` elements. If `n` is smaller than
+ // the inlined vector's current size, extra elements are destroyed. If `n` is
+ // larger than the initial size, new elements are value-initialized.
+ void resize(size_type n);
+
+ // Overload of `InlinedVector::resize()` to resize the inlined vector to
+ // contain `n` elements where, if `n` is larger than `size()`, the new values
+ // will be copy-constructed from `v`.
+ void resize(size_type n, const_reference v);
+
+ // `InlinedVector::insert()`
+ //
+ // Copies `v` into `position`, returning an `iterator` pointing to the newly
+ // inserted element.
+ iterator insert(const_iterator position, const_reference v) {
+ return emplace(position, v);
+ }
+
+ // Overload of `InlinedVector::insert()` for moving `v` into `position`,
+ // returning an iterator pointing to the newly inserted element.
+ iterator insert(const_iterator position, rvalue_reference v) {
+ return emplace(position, std::move(v));
+ }
+
+ // Overload of `InlinedVector::insert()` for inserting `n` contiguous copies
+ // of `v` starting at `position`. Returns an `iterator` pointing to the first
+ // of the newly inserted elements.
+ iterator insert(const_iterator position, size_type n, const_reference v) {
+ return InsertWithCount(position, n, v);
+ }
+
+ // Overload of `InlinedVector::insert()` for copying the contents of the
+ // `std::initializer_list` into the vector starting at `position`. Returns an
+ // `iterator` pointing to the first of the newly inserted elements.
+ iterator insert(const_iterator position,
+ std::initializer_list<value_type> init_list) {
+ return insert(position, init_list.begin(), init_list.end());
+ }
+
+ // Overload of `InlinedVector::insert()` for inserting elements constructed
+ // from the range [`first`, `last`). Returns an `iterator` pointing to the
+ // first of the newly inserted elements.
+ //
+ // NOTE: The `enable_if` is intended to disambiguate the two three-argument
+ // overloads of `insert()`.
+ template <typename InputIterator,
+ typename = EnableIfInputIterator<InputIterator>>
+ iterator insert(const_iterator position, InputIterator first,
+ InputIterator last) {
+ return InsertWithRange(position, first, last,
+ IteratorCategory<InputIterator>());
+ }
+
+ // `InlinedVector::emplace()`
+ //
+ // Constructs and inserts an object in the inlined vector at the given
+ // `position`, returning an `iterator` pointing to the newly emplaced element.
+ template <typename... Args>
+ iterator emplace(const_iterator position, Args&&... args);
+
+ // `InlinedVector::emplace_back()`
+ //
+ // Constructs and appends a new element to the end of the inlined vector,
+ // returning a `reference` to the emplaced element.
+ template <typename... Args>
+ reference emplace_back(Args&&... args) {
+ size_type s = size();
+ assert(s <= capacity());
+ if (ABSL_PREDICT_FALSE(s == capacity())) {
+ return GrowAndEmplaceBack(std::forward<Args>(args)...);
+ }
+ assert(s < capacity());
+
+ pointer space;
+ if (allocated()) {
+ tag().set_allocated_size(s + 1);
+ space = allocated_space();
+ } else {
+ tag().set_inline_size(s + 1);
+ space = inlined_space();
+ }
+ return Construct(space + s, std::forward<Args>(args)...);
+ }
+
+ // `InlinedVector::push_back()`
+ //
+ // Appends a copy of `v` to the end of the inlined vector.
+ void push_back(const_reference v) { static_cast<void>(emplace_back(v)); }
+
+ // Overload of `InlinedVector::push_back()` for moving `v` into a newly
+ // appended element.
+ void push_back(rvalue_reference v) {
+ static_cast<void>(emplace_back(std::move(v)));
+ }
+
+ // `InlinedVector::pop_back()`
+ //
+ // Destroys the element at the end of the inlined vector and shrinks the size
+ // by `1` (unless the inlined vector is empty, in which case this is a no-op).
+ void pop_back() noexcept {
+ assert(!empty());
+ size_type s = size();
+ if (allocated()) {
+ Destroy(allocated_space() + s - 1, allocated_space() + s);
+ tag().set_allocated_size(s - 1);
+ } else {
+ Destroy(inlined_space() + s - 1, inlined_space() + s);
+ tag().set_inline_size(s - 1);
+ }
+ }
+
+ // `InlinedVector::erase()`
+ //
+ // Erases the element at `position` of the inlined vector, returning an
+ // `iterator` pointing to the first element following the erased element.
+ //
+ // NOTE: May return the end iterator, which is not dereferencable.
+ iterator erase(const_iterator position) {
+ assert(position >= begin());
+ assert(position < end());
+
+ iterator pos = const_cast<iterator>(position);
+ std::move(pos + 1, end(), pos);
+ pop_back();
+ return pos;
+ }
+
+ // Overload of `InlinedVector::erase()` for erasing all elements in the
+ // range [`from`, `to`) in the inlined vector. Returns an `iterator` pointing
+ // to the first element following the range erased or the end iterator if `to`
+ // was the end iterator.
+ iterator erase(const_iterator from, const_iterator to);
+
+ // `InlinedVector::clear()`
+ //
+ // Destroys all elements in the inlined vector, sets the size of `0` and
+ // deallocates the heap allocation if the inlined vector was allocated.
+ void clear() noexcept {
+ size_type s = size();
+ if (allocated()) {
+ Destroy(allocated_space(), allocated_space() + s);
+ allocation().Dealloc(allocator());
+ } else if (s != 0) { // do nothing for empty vectors
+ Destroy(inlined_space(), inlined_space() + s);
+ }
+ tag() = Tag();
+ }
+
+ // `InlinedVector::reserve()`
+ //
+ // Enlarges the underlying representation of the inlined vector so it can hold
+ // at least `n` elements. This method does not change `size()` or the actual
+ // contents of the vector.
+ //
+ // NOTE: If `n` does not exceed `capacity()`, `reserve()` will have no
+ // effects. Otherwise, `reserve()` will reallocate, performing an n-time
+ // element-wise move of everything contained.
+ void reserve(size_type n) {
+ if (n > capacity()) {
+ // Make room for new elements
+ EnlargeBy(n - size());
+ }
+ }
+
+ // `InlinedVector::shrink_to_fit()`
+ //
+ // Reduces memory usage by freeing unused memory. After this call, calls to
+ // `capacity()` will be equal to `(std::max)(inlined_capacity(), size())`.
+ //
+ // If `size() <= inlined_capacity()` and the elements are currently stored on
+ // the heap, they will be moved to the inlined storage and the heap memory
+ // will be deallocated.
+ //
+ // If `size() > inlined_capacity()` and `size() < capacity()` the elements
+ // will be moved to a smaller heap allocation.
+ void shrink_to_fit() {
+ const auto s = size();
+ if (ABSL_PREDICT_FALSE(!allocated() || s == capacity())) return;
+
+ if (s <= inlined_capacity()) {
+ // Move the elements to the inlined storage.
+ // We have to do this using a temporary, because `inlined_storage` and
+ // `allocation_storage` are in a union field.
+ auto temp = std::move(*this);
+ assign(std::make_move_iterator(temp.begin()),
+ std::make_move_iterator(temp.end()));
+ return;
+ }
+
+ // Reallocate storage and move elements.
+ // We can't simply use the same approach as above, because `assign()` would
+ // call into `reserve()` internally and reserve larger capacity than we need
+ Allocation new_allocation(allocator(), s);
+ UninitializedCopy(std::make_move_iterator(allocated_space()),
+ std::make_move_iterator(allocated_space() + s),
+ new_allocation.buffer());
+ ResetAllocation(new_allocation, s);
+ }
+
+ // `InlinedVector::swap()`
+ //
+ // Swaps the contents of this inlined vector with the contents of `other`.
+ void swap(InlinedVector& other);
+
+ template <typename Hash>
+ friend Hash AbslHashValue(Hash hash, const InlinedVector& inlined_vector) {
+ const_pointer p = inlined_vector.data();
+ size_type n = inlined_vector.size();
+ return Hash::combine(Hash::combine_contiguous(std::move(hash), p, n), n);
+ }
+
+ private:
+ // Holds whether the vector is allocated or not in the lowest bit and the size
+ // in the high bits:
+ // `size_ = (size << 1) | is_allocated;`
+ class Tag {
+ public:
+ Tag() : size_(0) {}
+ size_type size() const { return size_ / 2; }
+ void add_size(size_type n) { size_ += n * 2; }
+ void set_inline_size(size_type n) { size_ = n * 2; }
+ void set_allocated_size(size_type n) { size_ = (n * 2) + 1; }
+ bool allocated() const { return size_ % 2; }
+
+ private:
+ size_type size_;
+ };
+
+ // Derives from `allocator_type` to use the empty base class optimization.
+ // If the `allocator_type` is stateless, we can store our instance for free.
+ class AllocatorAndTag : private allocator_type {
+ public:
+ explicit AllocatorAndTag(const allocator_type& a) : allocator_type(a) {}
+
+ Tag& tag() { return tag_; }
+ const Tag& tag() const { return tag_; }
+
+ allocator_type& allocator() { return *this; }
+ const allocator_type& allocator() const { return *this; }
+
+ private:
+ Tag tag_;
+ };
+
+ class Allocation {
+ public:
+ Allocation(allocator_type& a, size_type capacity)
+ : capacity_(capacity), buffer_(Create(a, capacity)) {}
+
+ void Dealloc(allocator_type& a) {
+ std::allocator_traits<allocator_type>::deallocate(a, buffer_, capacity_);
+ }
+
+ size_type capacity() const { return capacity_; }
+
+ const_pointer buffer() const { return buffer_; }
+
+ pointer buffer() { return buffer_; }
+
+ private:
+ static pointer Create(allocator_type& a, size_type n) {
+ return std::allocator_traits<allocator_type>::allocate(a, n);
+ }
+
+ size_type capacity_;
+ pointer buffer_;
+ };
+
+ const Tag& tag() const { return allocator_and_tag_.tag(); }
+
+ Tag& tag() { return allocator_and_tag_.tag(); }
+
+ Allocation& allocation() {
+ return reinterpret_cast<Allocation&>(rep_.allocation_storage.allocation);
+ }
+
+ const Allocation& allocation() const {
+ return reinterpret_cast<const Allocation&>(
+ rep_.allocation_storage.allocation);
+ }
+
+ void init_allocation(const Allocation& allocation) {
+ new (&rep_.allocation_storage.allocation) Allocation(allocation);
+ }
+
+ // TODO(absl-team): investigate whether the reinterpret_cast is appropriate.
+ pointer inlined_space() {
+ return reinterpret_cast<pointer>(
+ std::addressof(rep_.inlined_storage.inlined[0]));
+ }
+
+ const_pointer inlined_space() const {
+ return reinterpret_cast<const_pointer>(
+ std::addressof(rep_.inlined_storage.inlined[0]));
+ }
+
+ pointer allocated_space() { return allocation().buffer(); }
+
+ const_pointer allocated_space() const { return allocation().buffer(); }
+
+ const allocator_type& allocator() const {
+ return allocator_and_tag_.allocator();
+ }
+
+ allocator_type& allocator() { return allocator_and_tag_.allocator(); }
+
+ bool allocated() const { return tag().allocated(); }
+
+ // Enlarge the underlying representation so we can store `size_ + delta` elems
+ // in allocated space. The size is not changed, and any newly added memory is
+ // not initialized.
+ void EnlargeBy(size_type delta);
+
+ // Shift all elements from `position` to `end()` by `n` places to the right.
+ // If the vector needs to be enlarged, memory will be allocated.
+ // Returns `iterator`s pointing to the start of the previously-initialized
+ // portion and the start of the uninitialized portion of the created gap.
+ // The number of initialized spots is `pair.second - pair.first`. The number
+ // of raw spots is `n - (pair.second - pair.first)`.
+ //
+ // Updates the size of the InlinedVector internally.
+ std::pair<iterator, iterator> ShiftRight(const_iterator position,
+ size_type n);
+
+ void ResetAllocation(Allocation new_allocation, size_type new_size) {
+ if (allocated()) {
+ Destroy(allocated_space(), allocated_space() + size());
+ assert(begin() == allocated_space());
+ allocation().Dealloc(allocator());
+ allocation() = new_allocation;
+ } else {
+ Destroy(inlined_space(), inlined_space() + size());
+ init_allocation(new_allocation); // bug: only init once
+ }
+ tag().set_allocated_size(new_size);
+ }
+
+ template <typename... Args>
+ reference GrowAndEmplaceBack(Args&&... args) {
+ assert(size() == capacity());
+ const size_type s = size();
+
+ Allocation new_allocation(allocator(), 2 * capacity());
+
+ reference new_element =
+ Construct(new_allocation.buffer() + s, std::forward<Args>(args)...);
+ UninitializedCopy(std::make_move_iterator(data()),
+ std::make_move_iterator(data() + s),
+ new_allocation.buffer());
+
+ ResetAllocation(new_allocation, s + 1);
+
+ return new_element;
+ }
+
+ void InitAssign(size_type n);
+
+ void InitAssign(size_type n, const_reference v);
+
+ template <typename... Args>
+ reference Construct(pointer p, Args&&... args) {
+ std::allocator_traits<allocator_type>::construct(
+ allocator(), p, std::forward<Args>(args)...);
+ return *p;
+ }
+
+ template <typename Iterator>
+ void UninitializedCopy(Iterator src, Iterator src_last, pointer dst) {
+ for (; src != src_last; ++dst, ++src) Construct(dst, *src);
+ }
+
+ template <typename... Args>
+ void UninitializedFill(pointer dst, pointer dst_last, const Args&... args) {
+ for (; dst != dst_last; ++dst) Construct(dst, args...);
+ }
+
+ // Destroy [`from`, `to`) in place.
+ void Destroy(pointer from, pointer to);
+
+ template <typename Iterator>
+ void AppendRange(Iterator first, Iterator last, std::input_iterator_tag) {
+ std::copy(first, last, std::back_inserter(*this));
+ }
+
+ template <typename Iterator>
+ void AppendRange(Iterator first, Iterator last, std::forward_iterator_tag);
+
+ template <typename Iterator>
+ void AppendRange(Iterator first, Iterator last) {
+ AppendRange(first, last, IteratorCategory<Iterator>());
+ }
+
+ template <typename Iterator>
+ void AssignRange(Iterator first, Iterator last, std::input_iterator_tag);
+
+ template <typename Iterator>
+ void AssignRange(Iterator first, Iterator last, std::forward_iterator_tag);
+
+ template <typename Iterator>
+ void AssignRange(Iterator first, Iterator last) {
+ AssignRange(first, last, IteratorCategory<Iterator>());
+ }
+
+ iterator InsertWithCount(const_iterator position, size_type n,
+ const_reference v);
+
+ template <typename InputIterator>
+ iterator InsertWithRange(const_iterator position, InputIterator first,
+ InputIterator last, std::input_iterator_tag);
+
+ template <typename ForwardIterator>
+ iterator InsertWithRange(const_iterator position, ForwardIterator first,
+ ForwardIterator last, std::forward_iterator_tag);
+
+ // Stores either the inlined or allocated representation
+ union Rep {
+ using ValueTypeBuffer =
+ absl::aligned_storage_t<sizeof(value_type), alignof(value_type)>;
+ using AllocationBuffer =
+ absl::aligned_storage_t<sizeof(Allocation), alignof(Allocation)>;
+
+ // Structs wrap the buffers to perform indirection that solves a bizarre
+ // compilation error on Visual Studio (all known versions).
+ struct InlinedRep {
+ // `N` used in place of `inlined_capacity()` due to b/119696660
+ ValueTypeBuffer inlined[N];
+ };
+ struct AllocatedRep {
+ AllocationBuffer allocation;
+ };
+
+ InlinedRep inlined_storage;
+ AllocatedRep allocation_storage;
+ };
+
+ AllocatorAndTag allocator_and_tag_;
+ Rep rep_;
+};
+
+// -----------------------------------------------------------------------------
+// InlinedVector Non-Member Functions
+// -----------------------------------------------------------------------------
+
+// `swap()`
+//
+// Swaps the contents of two inlined vectors. This convenience function
+// simply calls `InlinedVector::swap()`.
+template <typename T, size_t N, typename A>
+void swap(InlinedVector<T, N, A>& a,
+ InlinedVector<T, N, A>& b) noexcept(noexcept(a.swap(b))) {
+ a.swap(b);
+}
+
+// `operator==()`
+//
+// Tests the equivalency of the contents of two inlined vectors.
+template <typename T, size_t N, typename A>
+bool operator==(const InlinedVector<T, N, A>& a,
+ const InlinedVector<T, N, A>& b) {
+ return absl::equal(a.begin(), a.end(), b.begin(), b.end());
+}
+
+// `operator!=()`
+//
+// Tests the inequality of the contents of two inlined vectors.
+template <typename T, size_t N, typename A>
+bool operator!=(const InlinedVector<T, N, A>& a,
+ const InlinedVector<T, N, A>& b) {
+ return !(a == b);
+}
+
+// `operator<()`
+//
+// Tests whether the contents of one inlined vector are less than the contents
+// of another through a lexicographical comparison operation.
+template <typename T, size_t N, typename A>
+bool operator<(const InlinedVector<T, N, A>& a,
+ const InlinedVector<T, N, A>& b) {
+ return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
+}
+
+// `operator>()`
+//
+// Tests whether the contents of one inlined vector are greater than the
+// contents of another through a lexicographical comparison operation.
+template <typename T, size_t N, typename A>
+bool operator>(const InlinedVector<T, N, A>& a,
+ const InlinedVector<T, N, A>& b) {
+ return b < a;
+}
+
+// `operator<=()`
+//
+// Tests whether the contents of one inlined vector are less than or equal to
+// the contents of another through a lexicographical comparison operation.
+template <typename T, size_t N, typename A>
+bool operator<=(const InlinedVector<T, N, A>& a,
+ const InlinedVector<T, N, A>& b) {
+ return !(b < a);
+}
+
+// `operator>=()`
+//
+// Tests whether the contents of one inlined vector are greater than or equal to
+// the contents of another through a lexicographical comparison operation.
+template <typename T, size_t N, typename A>
+bool operator>=(const InlinedVector<T, N, A>& a,
+ const InlinedVector<T, N, A>& b) {
+ return !(a < b);
+}
+
+// -----------------------------------------------------------------------------
+// Implementation of InlinedVector
+//
+// Do not depend on any below implementation details!
+// -----------------------------------------------------------------------------
+
+template <typename T, size_t N, typename A>
+InlinedVector<T, N, A>::InlinedVector(const InlinedVector& other)
+ : allocator_and_tag_(other.allocator()) {
+ reserve(other.size());
+ if (allocated()) {
+ UninitializedCopy(other.begin(), other.end(), allocated_space());
+ tag().set_allocated_size(other.size());
+ } else {
+ UninitializedCopy(other.begin(), other.end(), inlined_space());
+ tag().set_inline_size(other.size());
+ }
+}
+
+template <typename T, size_t N, typename A>
+InlinedVector<T, N, A>::InlinedVector(const InlinedVector& other,
+ const allocator_type& alloc)
+ : allocator_and_tag_(alloc) {
+ reserve(other.size());
+ if (allocated()) {
+ UninitializedCopy(other.begin(), other.end(), allocated_space());
+ tag().set_allocated_size(other.size());
+ } else {
+ UninitializedCopy(other.begin(), other.end(), inlined_space());
+ tag().set_inline_size(other.size());
+ }
+}
+
+template <typename T, size_t N, typename A>
+InlinedVector<T, N, A>::InlinedVector(InlinedVector&& other) noexcept(
+ absl::allocator_is_nothrow<allocator_type>::value ||
+ std::is_nothrow_move_constructible<value_type>::value)
+ : allocator_and_tag_(other.allocator_and_tag_) {
+ if (other.allocated()) {
+ // We can just steal the underlying buffer from the source.
+ // That leaves the source empty, so we clear its size.
+ init_allocation(other.allocation());
+ other.tag() = Tag();
+ } else {
+ UninitializedCopy(
+ std::make_move_iterator(other.inlined_space()),
+ std::make_move_iterator(other.inlined_space() + other.size()),
+ inlined_space());
+ }
+}
+
+template <typename T, size_t N, typename A>
+InlinedVector<T, N, A>::InlinedVector(InlinedVector&& other,
+ const allocator_type& alloc) noexcept( //
+ absl::allocator_is_nothrow<allocator_type>::value)
+ : allocator_and_tag_(alloc) {
+ if (other.allocated()) {
+ if (alloc == other.allocator()) {
+ // We can just steal the allocation from the source.
+ tag() = other.tag();
+ init_allocation(other.allocation());
+ other.tag() = Tag();
+ } else {
+ // We need to use our own allocator
+ reserve(other.size());
+ UninitializedCopy(std::make_move_iterator(other.begin()),
+ std::make_move_iterator(other.end()),
+ allocated_space());
+ tag().set_allocated_size(other.size());
+ }
+ } else {
+ UninitializedCopy(
+ std::make_move_iterator(other.inlined_space()),
+ std::make_move_iterator(other.inlined_space() + other.size()),
+ inlined_space());
+ tag().set_inline_size(other.size());
+ }
+}
+
+template <typename T, size_t N, typename A>
+void InlinedVector<T, N, A>::InitAssign(size_type n, const_reference v) {
+ if (n > inlined_capacity()) {
+ Allocation new_allocation(allocator(), n);
+ init_allocation(new_allocation);
+ UninitializedFill(allocated_space(), allocated_space() + n, v);
+ tag().set_allocated_size(n);
+ } else {
+ UninitializedFill(inlined_space(), inlined_space() + n, v);
+ tag().set_inline_size(n);
+ }
+}
+
+template <typename T, size_t N, typename A>
+void InlinedVector<T, N, A>::InitAssign(size_type n) {
+ if (n > inlined_capacity()) {
+ Allocation new_allocation(allocator(), n);
+ init_allocation(new_allocation);
+ UninitializedFill(allocated_space(), allocated_space() + n);
+ tag().set_allocated_size(n);
+ } else {
+ UninitializedFill(inlined_space(), inlined_space() + n);
+ tag().set_inline_size(n);
+ }
+}
+
+template <typename T, size_t N, typename A>
+void InlinedVector<T, N, A>::resize(size_type n) {
+ size_type s = size();
+ if (n < s) {
+ erase(begin() + n, end());
+ return;
+ }
+ reserve(n);
+ assert(capacity() >= n);
+
+ // Fill new space with elements constructed in-place.
+ if (allocated()) {
+ UninitializedFill(allocated_space() + s, allocated_space() + n);
+ tag().set_allocated_size(n);
+ } else {
+ UninitializedFill(inlined_space() + s, inlined_space() + n);
+ tag().set_inline_size(n);
+ }
+}
+
+template <typename T, size_t N, typename A>
+void InlinedVector<T, N, A>::resize(size_type n, const_reference v) {
+ size_type s = size();
+ if (n < s) {
+ erase(begin() + n, end());
+ return;
+ }
+ reserve(n);
+ assert(capacity() >= n);
+
+ // Fill new space with copies of 'v'.
+ if (allocated()) {
+ UninitializedFill(allocated_space() + s, allocated_space() + n, v);
+ tag().set_allocated_size(n);
+ } else {
+ UninitializedFill(inlined_space() + s, inlined_space() + n, v);
+ tag().set_inline_size(n);
+ }
+}
+
+template <typename T, size_t N, typename A>
+template <typename... Args>
+auto InlinedVector<T, N, A>::emplace(const_iterator position, Args&&... args)
+ -> iterator {
+ assert(position >= begin());
+ assert(position <= end());
+ if (ABSL_PREDICT_FALSE(position == end())) {
+ emplace_back(std::forward<Args>(args)...);
+ return end() - 1;
+ }
+
+ T new_t = T(std::forward<Args>(args)...);
+
+ auto range = ShiftRight(position, 1);
+ if (range.first == range.second) {
+ // constructing into uninitialized memory
+ Construct(range.first, std::move(new_t));
+ } else {
+ // assigning into moved-from object
+ *range.first = T(std::move(new_t));
+ }
+
+ return range.first;
+}
+
+template <typename T, size_t N, typename A>
+auto InlinedVector<T, N, A>::erase(const_iterator from, const_iterator to)
+ -> iterator {
+ assert(begin() <= from);
+ assert(from <= to);
+ assert(to <= end());
+
+ iterator range_start = const_cast<iterator>(from);
+ iterator range_end = const_cast<iterator>(to);
+
+ size_type s = size();
+ ptrdiff_t erase_gap = std::distance(range_start, range_end);
+ if (erase_gap > 0) {
+ pointer space;
+ if (allocated()) {
+ space = allocated_space();
+ tag().set_allocated_size(s - erase_gap);
+ } else {
+ space = inlined_space();
+ tag().set_inline_size(s - erase_gap);
+ }
+ std::move(range_end, space + s, range_start);
+ Destroy(space + s - erase_gap, space + s);
+ }
+ return range_start;
+}
+
+template <typename T, size_t N, typename A>
+void InlinedVector<T, N, A>::swap(InlinedVector& other) {
+ using std::swap; // Augment ADL with `std::swap`.
+ if (ABSL_PREDICT_FALSE(this == &other)) return;
+
+ if (allocated() && other.allocated()) {
+ // Both out of line, so just swap the tag, allocation, and allocator.
+ swap(tag(), other.tag());
+ swap(allocation(), other.allocation());
+ swap(allocator(), other.allocator());
+ return;
+ }
+ if (!allocated() && !other.allocated()) {
+ // Both inlined: swap up to smaller size, then move remaining elements.
+ InlinedVector* a = this;
+ InlinedVector* b = &other;
+ if (size() < other.size()) {
+ swap(a, b);
+ }
+
+ const size_type a_size = a->size();
+ const size_type b_size = b->size();
+ assert(a_size >= b_size);
+ // `a` is larger. Swap the elements up to the smaller array size.
+ std::swap_ranges(a->inlined_space(), a->inlined_space() + b_size,
+ b->inlined_space());
+
+ // Move the remaining elements:
+ // [`b_size`, `a_size`) from `a` -> [`b_size`, `a_size`) from `b`
+ b->UninitializedCopy(a->inlined_space() + b_size,
+ a->inlined_space() + a_size,
+ b->inlined_space() + b_size);
+ a->Destroy(a->inlined_space() + b_size, a->inlined_space() + a_size);
+
+ swap(a->tag(), b->tag());
+ swap(a->allocator(), b->allocator());
+ assert(b->size() == a_size);
+ assert(a->size() == b_size);
+ return;
+ }
+
+ // One is out of line, one is inline.
+ // We first move the elements from the inlined vector into the
+ // inlined space in the other vector. We then put the other vector's
+ // pointer/capacity into the originally inlined vector and swap
+ // the tags.
+ InlinedVector* a = this;
+ InlinedVector* b = &other;
+ if (a->allocated()) {
+ swap(a, b);
+ }
+ assert(!a->allocated());
+ assert(b->allocated());
+ const size_type a_size = a->size();
+ const size_type b_size = b->size();
+ // In an optimized build, `b_size` would be unused.
+ static_cast<void>(b_size);
+
+ // Made Local copies of `size()`, don't need `tag()` accurate anymore
+ swap(a->tag(), b->tag());
+
+ // Copy `b_allocation` out before `b`'s union gets clobbered by `inline_space`
+ Allocation b_allocation = b->allocation();
+
+ b->UninitializedCopy(a->inlined_space(), a->inlined_space() + a_size,
+ b->inlined_space());
+ a->Destroy(a->inlined_space(), a->inlined_space() + a_size);
+
+ a->allocation() = b_allocation;
+
+ if (a->allocator() != b->allocator()) {
+ swap(a->allocator(), b->allocator());
+ }
+
+ assert(b->size() == a_size);
+ assert(a->size() == b_size);
+}
+
+template <typename T, size_t N, typename A>
+void InlinedVector<T, N, A>::EnlargeBy(size_type delta) {
+ const size_type s = size();
+ assert(s <= capacity());
+
+ size_type target = std::max(inlined_capacity(), s + delta);
+
+ // Compute new capacity by repeatedly doubling current capacity
+ // TODO(user): Check and avoid overflow?
+ size_type new_capacity = capacity();
+ while (new_capacity < target) {
+ new_capacity <<= 1;
+ }
+
+ Allocation new_allocation(allocator(), new_capacity);
+
+ UninitializedCopy(std::make_move_iterator(data()),
+ std::make_move_iterator(data() + s),
+ new_allocation.buffer());
+
+ ResetAllocation(new_allocation, s);
+}
+
+template <typename T, size_t N, typename A>
+auto InlinedVector<T, N, A>::ShiftRight(const_iterator position, size_type n)
+ -> std::pair<iterator, iterator> {
+ iterator start_used = const_cast<iterator>(position);
+ iterator start_raw = const_cast<iterator>(position);
+ size_type s = size();
+ size_type required_size = s + n;
+
+ if (required_size > capacity()) {
+ // Compute new capacity by repeatedly doubling current capacity
+ size_type new_capacity = capacity();
+ while (new_capacity < required_size) {
+ new_capacity <<= 1;
+ }
+ // Move everyone into the new allocation, leaving a gap of `n` for the
+ // requested shift.
+ Allocation new_allocation(allocator(), new_capacity);
+ size_type index = position - begin();
+ UninitializedCopy(std::make_move_iterator(data()),
+ std::make_move_iterator(data() + index),
+ new_allocation.buffer());
+ UninitializedCopy(std::make_move_iterator(data() + index),
+ std::make_move_iterator(data() + s),
+ new_allocation.buffer() + index + n);
+ ResetAllocation(new_allocation, s);
+
+ // New allocation means our iterator is invalid, so we'll recalculate.
+ // Since the entire gap is in new space, there's no used space to reuse.
+ start_raw = begin() + index;
+ start_used = start_raw;
+ } else {
+ // If we had enough space, it's a two-part move. Elements going into
+ // previously-unoccupied space need an `UninitializedCopy()`. Elements
+ // going into a previously-occupied space are just a `std::move()`.
+ iterator pos = const_cast<iterator>(position);
+ iterator raw_space = end();
+ size_type slots_in_used_space = raw_space - pos;
+ size_type new_elements_in_used_space = std::min(n, slots_in_used_space);
+ size_type new_elements_in_raw_space = n - new_elements_in_used_space;
+ size_type old_elements_in_used_space =
+ slots_in_used_space - new_elements_in_used_space;
+
+ UninitializedCopy(std::make_move_iterator(pos + old_elements_in_used_space),
+ std::make_move_iterator(raw_space),
+ raw_space + new_elements_in_raw_space);
+ std::move_backward(pos, pos + old_elements_in_used_space, raw_space);
+
+ // If the gap is entirely in raw space, the used space starts where the raw
+ // space starts, leaving no elements in used space. If the gap is entirely
+ // in used space, the raw space starts at the end of the gap, leaving all
+ // elements accounted for within the used space.
+ start_used = pos;
+ start_raw = pos + new_elements_in_used_space;
+ }
+ tag().add_size(n);
+ return std::make_pair(start_used, start_raw);
+}
+
+template <typename T, size_t N, typename A>
+void InlinedVector<T, N, A>::Destroy(pointer from, pointer to) {
+ for (pointer cur = from; cur != to; ++cur) {
+ std::allocator_traits<allocator_type>::destroy(allocator(), cur);
+ }
+#ifndef NDEBUG
+ // Overwrite unused memory with `0xab` so we can catch uninitialized usage.
+ // Cast to `void*` to tell the compiler that we don't care that we might be
+ // scribbling on a vtable pointer.
+ if (from != to) {
+ auto len = sizeof(value_type) * std::distance(from, to);
+ std::memset(reinterpret_cast<void*>(from), 0xab, len);
+ }
+#endif
+}
+
+template <typename T, size_t N, typename A>
+template <typename Iterator>
+void InlinedVector<T, N, A>::AppendRange(Iterator first, Iterator last,
+ std::forward_iterator_tag) {
+ auto length = std::distance(first, last);
+ reserve(size() + length);
+ if (allocated()) {
+ UninitializedCopy(first, last, allocated_space() + size());
+ tag().set_allocated_size(size() + length);
+ } else {
+ UninitializedCopy(first, last, inlined_space() + size());
+ tag().set_inline_size(size() + length);
+ }
+}
+
+template <typename T, size_t N, typename A>
+template <typename Iterator>
+void InlinedVector<T, N, A>::AssignRange(Iterator first, Iterator last,
+ std::input_iterator_tag) {
+ // Optimized to avoid reallocation.
+ // Prefer reassignment to copy construction for elements.
+ iterator out = begin();
+ for (; first != last && out != end(); ++first, ++out) {
+ *out = *first;
+ }
+ erase(out, end());
+ std::copy(first, last, std::back_inserter(*this));
+}
+
+template <typename T, size_t N, typename A>
+template <typename Iterator>
+void InlinedVector<T, N, A>::AssignRange(Iterator first, Iterator last,
+ std::forward_iterator_tag) {
+ auto length = std::distance(first, last);
+ // Prefer reassignment to copy construction for elements.
+ if (static_cast<size_type>(length) <= size()) {
+ erase(std::copy(first, last, begin()), end());
+ return;
+ }
+ reserve(length);
+ iterator out = begin();
+ for (; out != end(); ++first, ++out) *out = *first;
+ if (allocated()) {
+ UninitializedCopy(first, last, out);
+ tag().set_allocated_size(length);
+ } else {
+ UninitializedCopy(first, last, out);
+ tag().set_inline_size(length);
+ }
+}
+
+template <typename T, size_t N, typename A>
+auto InlinedVector<T, N, A>::InsertWithCount(const_iterator position,
+ size_type n, const_reference v)
+ -> iterator {
+ assert(position >= begin() && position <= end());
+ if (ABSL_PREDICT_FALSE(n == 0)) return const_cast<iterator>(position);
+
+ value_type copy = v;
+ std::pair<iterator, iterator> it_pair = ShiftRight(position, n);
+ std::fill(it_pair.first, it_pair.second, copy);
+ UninitializedFill(it_pair.second, it_pair.first + n, copy);
+
+ return it_pair.first;
+}
+
+template <typename T, size_t N, typename A>
+template <typename InputIterator>
+auto InlinedVector<T, N, A>::InsertWithRange(const_iterator position,
+ InputIterator first,
+ InputIterator last,
+ std::input_iterator_tag)
+ -> iterator {
+ assert(position >= begin() && position <= end());
+ size_type index = position - cbegin();
+ size_type i = index;
+ while (first != last) insert(begin() + i++, *first++);
+ return begin() + index;
+}
+
+template <typename T, size_t N, typename A>
+template <typename ForwardIterator>
+auto InlinedVector<T, N, A>::InsertWithRange(const_iterator position,
+ ForwardIterator first,
+ ForwardIterator last,
+ std::forward_iterator_tag)
+ -> iterator {
+ assert(position >= begin() && position <= end());
+ if (ABSL_PREDICT_FALSE(first == last)) return const_cast<iterator>(position);
+
+ auto n = std::distance(first, last);
+ std::pair<iterator, iterator> it_pair = ShiftRight(position, n);
+ size_type used_spots = it_pair.second - it_pair.first;
+ ForwardIterator open_spot = std::next(first, used_spots);
+ std::copy(first, open_spot, it_pair.first);
+ UninitializedCopy(open_spot, last, it_pair.second);
+ return it_pair.first;
+}
+
+} // namespace absl
+
+
+#endif // S2_THIRD_PARTY_ABSL_CONTAINER_INLINED_VECTOR_H_
--- /dev/null
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Helper class to perform the Empty Base Optimization.
+// Ts can contain classes and non-classes, empty or not. For the ones that
+// are empty classes, we perform the optimization. If all types in Ts are empty
+// classes, then CompressedTuple<Ts...> is itself an empty class.
+//
+// To access the members, use member get<N>() function.
+//
+// Eg:
+// absl::container_internal::CompressedTuple<int, T1, T2, T3> value(7, t1, t2,
+// t3);
+// assert(value.get<0>() == 7);
+// T1& t1 = value.get<1>();
+// const T2& t2 = value.get<2>();
+// ...
+//
+// http://en.cppreference.com/w/cpp/language/ebo
+
+#ifndef S2_THIRD_PARTY_ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_
+#define S2_THIRD_PARTY_ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_
+
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "s2/third_party/absl/utility/utility.h"
+
+#ifdef _MSC_VER
+// We need to mark these classes with this declspec to ensure that
+// CompressedTuple happens.
+#define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC __declspec(empty_bases)
+#else // _MSC_VER
+#define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC
+#endif // _MSC_VER
+
+namespace absl {
+namespace container_internal {
+
+template <typename... Ts>
+class CompressedTuple;
+
+namespace internal_compressed_tuple {
+
+template <typename D, size_t I>
+struct Elem;
+template <typename... B, size_t I>
+struct Elem<CompressedTuple<B...>, I>
+ : std::tuple_element<I, std::tuple<B...>> {};
+template <typename D, size_t I>
+using ElemT = typename Elem<D, I>::type;
+
+// Use the __is_final intrinsic if available. Where it's not available, classes
+// declared with the 'final' specifier cannot be used as CompressedTuple
+// elements.
+// TODO(user): Replace this with std::is_final in C++14.
+template <typename T>
+constexpr bool IsFinal() {
+#if defined(__clang__) || defined(__GNUC__)
+ return __is_final(T);
+#else
+ return false;
+#endif
+}
+
+template <typename T>
+constexpr bool ShouldUseBase() {
+ return std::is_class<T>::value && std::is_empty<T>::value && !IsFinal<T>();
+}
+
+// The storage class provides two specializations:
+// - For empty classes, it stores T as a base class.
+// - For everything else, it stores T as a member.
+template <typename D, size_t I, bool = ShouldUseBase<ElemT<D, I>>()>
+struct Storage {
+ using T = ElemT<D, I>;
+ T value;
+ constexpr Storage() = default;
+ explicit constexpr Storage(T&& v) : value(absl::forward<T>(v)) {}
+ constexpr const T& get() const& { return value; }
+ T& get() & { return value; }
+ constexpr const T&& get() const&& { return absl::move(*this).value; }
+ T&& get() && { return std::move(*this).value; }
+};
+
+template <typename D, size_t I>
+struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC Storage<D, I, true>
+ : ElemT<D, I> {
+ using T = internal_compressed_tuple::ElemT<D, I>;
+ constexpr Storage() = default;
+ explicit constexpr Storage(T&& v) : T(absl::forward<T>(v)) {}
+ constexpr const T& get() const& { return *this; }
+ T& get() & { return *this; }
+ constexpr const T&& get() const&& { return absl::move(*this); }
+ T&& get() && { return std::move(*this); }
+};
+
+template <typename D, typename I>
+struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl;
+
+template <typename... Ts, size_t... I>
+struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC
+ CompressedTupleImpl<CompressedTuple<Ts...>, absl::index_sequence<I...>>
+ // We use the dummy identity function through std::integral_constant to
+ // convince MSVC of accepting and expanding I in that context. Without it
+ // you would get:
+ // error C3548: 'I': parameter pack cannot be used in this context
+ : Storage<CompressedTuple<Ts...>,
+ std::integral_constant<size_t, I>::value>... {
+ constexpr CompressedTupleImpl() = default;
+ explicit constexpr CompressedTupleImpl(Ts&&... args)
+ : Storage<CompressedTuple<Ts...>, I>(absl::forward<Ts>(args))... {}
+};
+
+} // namespace internal_compressed_tuple
+
+// Helper class to perform the Empty Base Class Optimization.
+// Ts can contain classes and non-classes, empty or not. For the ones that
+// are empty classes, we perform the CompressedTuple. If all types in Ts are
+// empty classes, then CompressedTuple<Ts...> is itself an empty class.
+//
+// To access the members, use member .get<N>() function.
+//
+// Eg:
+// absl::container_internal::CompressedTuple<int, T1, T2, T3> value(7, t1, t2,
+// t3);
+// assert(value.get<0>() == 7);
+// T1& t1 = value.get<1>();
+// const T2& t2 = value.get<2>();
+// ...
+//
+// http://en.cppreference.com/w/cpp/language/ebo
+template <typename... Ts>
+class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple
+ : private internal_compressed_tuple::CompressedTupleImpl<
+ CompressedTuple<Ts...>, absl::index_sequence_for<Ts...>> {
+ private:
+ template <int I>
+ using ElemT = internal_compressed_tuple::ElemT<CompressedTuple, I>;
+
+ public:
+ constexpr CompressedTuple() = default;
+ explicit constexpr CompressedTuple(Ts... base)
+ : CompressedTuple::CompressedTupleImpl(absl::forward<Ts>(base)...) {}
+
+ template <int I>
+ ElemT<I>& get() & {
+ return internal_compressed_tuple::Storage<CompressedTuple, I>::get();
+ }
+
+ template <int I>
+ constexpr const ElemT<I>& get() const& {
+ return internal_compressed_tuple::Storage<CompressedTuple, I>::get();
+ }
+
+ template <int I>
+ ElemT<I>&& get() && {
+ return std::move(*this)
+ .internal_compressed_tuple::template Storage<CompressedTuple, I>::get();
+ }
+
+ template <int I>
+ constexpr const ElemT<I>&& get() const&& {
+ return absl::move(*this)
+ .internal_compressed_tuple::template Storage<CompressedTuple, I>::get();
+ }
+};
+
+// Explicit specialization for a zero-element tuple
+// (needed to avoid ambiguous overloads for the default constructor).
+template <>
+class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple<> {};
+
+} // namespace container_internal
+} // namespace absl
+
+#undef ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC
+
+#endif // S2_THIRD_PARTY_ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_
--- /dev/null
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef S2_THIRD_PARTY_ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_
+#define S2_THIRD_PARTY_ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_
+
+#ifdef ADDRESS_SANITIZER
+#include <sanitizer/asan_interface.h>
+#endif
+
+#ifdef MEMORY_SANITIZER
+#include <sanitizer/msan_interface.h>
+#endif
+
+#include <cassert>
+#include <cstddef>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/third_party/absl/utility/utility.h"
+
+namespace absl {
+namespace container_internal {
+
+// Allocates at least n bytes aligned to the specified alignment.
+// Alignment must be a power of 2. It must be positive.
+//
+// Note that many allocators don't honor alignment requirements above certain
+// threshold (usually either alignof(std::max_align_t) or alignof(void*)).
+// Allocate() doesn't apply alignment corrections. If the underlying allocator
+// returns insufficiently alignment pointer, that's what you are going to get.
+template <size_t Alignment, class Alloc>
+void* Allocate(Alloc* alloc, size_t n) {
+ static_assert(Alignment > 0, "");
+ assert(n && "n must be positive");
+ struct alignas(Alignment) M {};
+ using A = typename absl::allocator_traits<Alloc>::template rebind_alloc<M>;
+ using AT = typename absl::allocator_traits<Alloc>::template rebind_traits<M>;
+ A mem_alloc(*alloc);
+ void* p = AT::allocate(mem_alloc, (n + sizeof(M) - 1) / sizeof(M));
+ assert(reinterpret_cast<uintptr_t>(p) % Alignment == 0 &&
+ "allocator does not respect alignment");
+ return p;
+}
+
+// The pointer must have been previously obtained by calling
+// Allocate<Alignment>(alloc, n).
+template <size_t Alignment, class Alloc>
+void Deallocate(Alloc* alloc, void* p, size_t n) {
+ static_assert(Alignment > 0, "");
+ assert(n && "n must be positive");
+ struct alignas(Alignment) M {};
+ using A = typename absl::allocator_traits<Alloc>::template rebind_alloc<M>;
+ using AT = typename absl::allocator_traits<Alloc>::template rebind_traits<M>;
+ A mem_alloc(*alloc);
+ AT::deallocate(mem_alloc, static_cast<M*>(p),
+ (n + sizeof(M) - 1) / sizeof(M));
+}
+
+namespace memory_internal {
+
+// Constructs T into uninitialized storage pointed by `ptr` using the args
+// specified in the tuple.
+template <class Alloc, class T, class Tuple, size_t... I>
+void ConstructFromTupleImpl(Alloc* alloc, T* ptr, Tuple&& t,
+ absl::index_sequence<I...>) {
+ absl::allocator_traits<Alloc>::construct(
+ *alloc, ptr, std::get<I>(std::forward<Tuple>(t))...);
+}
+
+template <class T, class F>
+struct WithConstructedImplF {
+ template <class... Args>
+ decltype(std::declval<F>()(std::declval<T>())) operator()(
+ Args&&... args) const {
+ return std::forward<F>(f)(T(std::forward<Args>(args)...));
+ }
+ F&& f;
+};
+
+template <class T, class Tuple, size_t... Is, class F>
+decltype(std::declval<F>()(std::declval<T>())) WithConstructedImpl(
+ Tuple&& t, absl::index_sequence<Is...>, F&& f) {
+ return WithConstructedImplF<T, F>{std::forward<F>(f)}(
+ std::get<Is>(std::forward<Tuple>(t))...);
+}
+
+template <class T, size_t... Is>
+auto TupleRefImpl(T&& t, absl::index_sequence<Is...>)
+ -> decltype(std::forward_as_tuple(std::get<Is>(std::forward<T>(t))...)) {
+ return std::forward_as_tuple(std::get<Is>(std::forward<T>(t))...);
+}
+
+// Returns a tuple of references to the elements of the input tuple. T must be a
+// tuple.
+template <class T>
+auto TupleRef(T&& t) -> decltype(
+ TupleRefImpl(std::forward<T>(t),
+ absl::make_index_sequence<
+ std::tuple_size<typename std::decay<T>::type>::value>())) {
+ return TupleRefImpl(
+ std::forward<T>(t),
+ absl::make_index_sequence<
+ std::tuple_size<typename std::decay<T>::type>::value>());
+}
+
+template <class F, class K, class V>
+decltype(std::declval<F>()(std::declval<const K&>(), std::piecewise_construct,
+ std::declval<std::tuple<K>>(), std::declval<V>()))
+DecomposePairImpl(F&& f, std::pair<std::tuple<K>, V> p) {
+ const auto& key = std::get<0>(p.first);
+ return std::forward<F>(f)(key, std::piecewise_construct, std::move(p.first),
+ std::move(p.second));
+}
+
+} // namespace memory_internal
+
+// Constructs T into uninitialized storage pointed by `ptr` using the args
+// specified in the tuple.
+template <class Alloc, class T, class Tuple>
+void ConstructFromTuple(Alloc* alloc, T* ptr, Tuple&& t) {
+ memory_internal::ConstructFromTupleImpl(
+ alloc, ptr, std::forward<Tuple>(t),
+ absl::make_index_sequence<
+ std::tuple_size<typename std::decay<Tuple>::type>::value>());
+}
+
+// Constructs T using the args specified in the tuple and calls F with the
+// constructed value.
+template <class T, class Tuple, class F>
+decltype(std::declval<F>()(std::declval<T>())) WithConstructed(
+ Tuple&& t, F&& f) {
+ return memory_internal::WithConstructedImpl<T>(
+ std::forward<Tuple>(t),
+ absl::make_index_sequence<
+ std::tuple_size<typename std::decay<Tuple>::type>::value>(),
+ std::forward<F>(f));
+}
+
+// Given arguments of an std::pair's consructor, PairArgs() returns a pair of
+// tuples with references to the passed arguments. The tuples contain
+// constructor arguments for the first and the second elements of the pair.
+//
+// The following two snippets are equivalent.
+//
+// 1. std::pair<F, S> p(args...);
+//
+// 2. auto a = PairArgs(args...);
+// std::pair<F, S> p(std::piecewise_construct,
+// std::move(p.first), std::move(p.second));
+inline std::pair<std::tuple<>, std::tuple<>> PairArgs() { return {}; }
+template <class F, class S>
+std::pair<std::tuple<F&&>, std::tuple<S&&>> PairArgs(F&& f, S&& s) {
+ return {std::piecewise_construct, std::forward_as_tuple(std::forward<F>(f)),
+ std::forward_as_tuple(std::forward<S>(s))};
+}
+template <class F, class S>
+std::pair<std::tuple<const F&>, std::tuple<const S&>> PairArgs(
+ const std::pair<F, S>& p) {
+ return PairArgs(p.first, p.second);
+}
+template <class F, class S>
+std::pair<std::tuple<F&&>, std::tuple<S&&>> PairArgs(std::pair<F, S>&& p) {
+ return PairArgs(std::forward<F>(p.first), std::forward<S>(p.second));
+}
+template <class F, class S>
+auto PairArgs(std::piecewise_construct_t, F&& f, S&& s)
+ -> decltype(std::make_pair(memory_internal::TupleRef(std::forward<F>(f)),
+ memory_internal::TupleRef(std::forward<S>(s)))) {
+ return std::make_pair(memory_internal::TupleRef(std::forward<F>(f)),
+ memory_internal::TupleRef(std::forward<S>(s)));
+}
+
+// A helper function for implementing apply() in map policies.
+template <class F, class... Args>
+auto DecomposePair(F&& f, Args&&... args)
+ -> decltype(memory_internal::DecomposePairImpl(
+ std::forward<F>(f), PairArgs(std::forward<Args>(args)...))) {
+ return memory_internal::DecomposePairImpl(
+ std::forward<F>(f), PairArgs(std::forward<Args>(args)...));
+}
+
+// A helper function for implementing apply() in set policies.
+template <class F, class Arg>
+decltype(std::declval<F>()(std::declval<const Arg&>(), std::declval<Arg>()))
+DecomposeValue(F&& f, Arg&& arg) {
+ const auto& key = arg;
+ return std::forward<F>(f)(key, std::forward<Arg>(arg));
+}
+
+// Helper functions for asan and msan.
+inline void SanitizerPoisonMemoryRegion(const void* m, size_t s) {
+#ifdef ADDRESS_SANITIZER
+ ASAN_POISON_MEMORY_REGION(m, s);
+#endif
+#ifdef MEMORY_SANITIZER
+ __msan_poison(m, s);
+#endif
+ (void)m;
+ (void)s;
+}
+
+inline void SanitizerUnpoisonMemoryRegion(const void* m, size_t s) {
+#ifdef ADDRESS_SANITIZER
+ ASAN_UNPOISON_MEMORY_REGION(m, s);
+#endif
+#ifdef MEMORY_SANITIZER
+ __msan_unpoison(m, s);
+#endif
+ (void)m;
+ (void)s;
+}
+
+template <typename T>
+inline void SanitizerPoisonObject(const T* object) {
+ SanitizerPoisonMemoryRegion(object, sizeof(T));
+}
+
+template <typename T>
+inline void SanitizerUnpoisonObject(const T* object) {
+ SanitizerUnpoisonMemoryRegion(object, sizeof(T));
+}
+
+namespace memory_internal {
+
+// If Pair is a standard-layout type, OffsetOf<Pair>::kFirst and
+// OffsetOf<Pair>::kSecond are equivalent to offsetof(Pair, first) and
+// offsetof(Pair, second) respectively. Otherwise they are -1.
+//
+// The purpose of OffsetOf is to avoid calling offsetof() on non-standard-layout
+// type, which is non-portable.
+template <class Pair, class = std::true_type>
+struct OffsetOf {
+ static constexpr size_t kFirst = -1;
+ static constexpr size_t kSecond = -1;
+};
+
+template <class Pair>
+struct OffsetOf<Pair, typename std::is_standard_layout<Pair>::type> {
+ static constexpr size_t kFirst = offsetof(Pair, first);
+ static constexpr size_t kSecond = offsetof(Pair, second);
+};
+
+template <class K, class V>
+struct IsLayoutCompatible {
+ private:
+ struct Pair {
+ K first;
+ V second;
+ };
+
+ // Is P layout-compatible with Pair?
+ template <class P>
+ static constexpr bool LayoutCompatible() {
+ return std::is_standard_layout<P>() && sizeof(P) == sizeof(Pair) &&
+ alignof(P) == alignof(Pair) &&
+ memory_internal::OffsetOf<P>::kFirst ==
+ memory_internal::OffsetOf<Pair>::kFirst &&
+ memory_internal::OffsetOf<P>::kSecond ==
+ memory_internal::OffsetOf<Pair>::kSecond;
+ }
+
+ public:
+ // Whether pair<const K, V> and pair<K, V> are layout-compatible. If they are,
+ // then it is safe to store them in a union and read from either.
+ static constexpr bool value = std::is_standard_layout<K>() &&
+ std::is_standard_layout<Pair>() &&
+ memory_internal::OffsetOf<Pair>::kFirst == 0 &&
+ LayoutCompatible<std::pair<K, V>>() &&
+ LayoutCompatible<std::pair<const K, V>>();
+};
+
+} // namespace memory_internal
+
+// The internal storage type for key-value containers like flat_hash_map.
+//
+// It is convenient for the value_type of a flat_hash_map<K, V> to be
+// pair<const K, V>; the "const K" prevents accidental modification of the key
+// when dealing with the reference returned from find() and similar methods.
+// However, this creates other problems; we want to be able to emplace(K, V)
+// efficiently with move operations, and similarly be able to move a
+// pair<K, V> in insert().
+//
+// The solution is this union, which aliases the const and non-const versions
+// of the pair. This also allows flat_hash_map<const K, V> to work, even though
+// that has the same efficiency issues with move in emplace() and insert() -
+// but people do it anyway.
+//
+// If kMutableKeys is false, only the value member can be accessed.
+//
+// If kMutableKeys is true, key can be accessed through all slots while value
+// and mutable_value must be accessed only via INITIALIZED slots. Slots are
+// created and destroyed via mutable_value so that the key can be moved later.
+//
+// Accessing one of the union fields while the other is active is safe as
+// long as they are layout-compatible, which is guaranteed by the definition of
+// kMutableKeys. For C++11, the relevant section of the standard is
+// https://timsong-cpp.github.io/cppwp/n3337/class.mem#19 (9.2.19)
+template <class K, class V>
+union slot_type {
+ private:
+ static void emplace(slot_type* slot) {
+ // The construction of union doesn't do anything at runtime but it allows us
+ // to access its members without violating aliasing rules.
+ new (slot) slot_type;
+ }
+ // If pair<const K, V> and pair<K, V> are layout-compatible, we can accept one
+ // or the other via slot_type. We are also free to access the key via
+ // slot_type::key in this case.
+ using kMutableKeys =
+ std::integral_constant<bool,
+ memory_internal::IsLayoutCompatible<K, V>::value>;
+
+ public:
+ slot_type() {}
+ ~slot_type() = delete;
+ using value_type = std::pair<const K, V>;
+ using mutable_value_type = std::pair<K, V>;
+
+ value_type value;
+ mutable_value_type mutable_value;
+ K key;
+
+ template <class Allocator, class... Args>
+ static void construct(Allocator* alloc, slot_type* slot, Args&&... args) {
+ emplace(slot);
+ if (kMutableKeys::value) {
+ absl::allocator_traits<Allocator>::construct(*alloc, &slot->mutable_value,
+ std::forward<Args>(args)...);
+ } else {
+ absl::allocator_traits<Allocator>::construct(*alloc, &slot->value,
+ std::forward<Args>(args)...);
+ }
+ }
+
+ // Construct this slot by moving from another slot.
+ template <class Allocator>
+ static void construct(Allocator* alloc, slot_type* slot, slot_type* other) {
+ emplace(slot);
+ if (kMutableKeys::value) {
+ absl::allocator_traits<Allocator>::construct(
+ *alloc, &slot->mutable_value, std::move(other->mutable_value));
+ } else {
+ absl::allocator_traits<Allocator>::construct(*alloc, &slot->value,
+ std::move(other->value));
+ }
+ }
+
+ template <class Allocator>
+ static void destroy(Allocator* alloc, slot_type* slot) {
+ if (kMutableKeys::value) {
+ absl::allocator_traits<Allocator>::destroy(*alloc, &slot->mutable_value);
+ } else {
+ absl::allocator_traits<Allocator>::destroy(*alloc, &slot->value);
+ }
+ }
+
+ template <class Allocator>
+ static void transfer(Allocator* alloc, slot_type* new_slot,
+ slot_type* old_slot) {
+ emplace(new_slot);
+ if (kMutableKeys::value) {
+ absl::allocator_traits<Allocator>::construct(
+ *alloc, &new_slot->mutable_value, std::move(old_slot->mutable_value));
+ } else {
+ absl::allocator_traits<Allocator>::construct(*alloc, &new_slot->value,
+ std::move(old_slot->value));
+ }
+ destroy(alloc, old_slot);
+ }
+
+ template <class Allocator>
+ static void swap(Allocator* alloc, slot_type* a, slot_type* b) {
+ if (kMutableKeys::value) {
+ using std::swap;
+ swap(a->mutable_value, b->mutable_value);
+ } else {
+ value_type tmp = std::move(a->value);
+ absl::allocator_traits<Allocator>::destroy(*alloc, &a->value);
+ absl::allocator_traits<Allocator>::construct(*alloc, &a->value,
+ std::move(b->value));
+ absl::allocator_traits<Allocator>::destroy(*alloc, &b->value);
+ absl::allocator_traits<Allocator>::construct(*alloc, &b->value,
+ std::move(tmp));
+ }
+ }
+
+ template <class Allocator>
+ static void move(Allocator* alloc, slot_type* src, slot_type* dest) {
+ if (kMutableKeys::value) {
+ dest->mutable_value = std::move(src->mutable_value);
+ } else {
+ absl::allocator_traits<Allocator>::destroy(*alloc, &dest->value);
+ absl::allocator_traits<Allocator>::construct(*alloc, &dest->value,
+ std::move(src->value));
+ }
+ }
+
+ template <class Allocator>
+ static void move(Allocator* alloc, slot_type* first, slot_type* last,
+ slot_type* result) {
+ for (slot_type *src = first, *dest = result; src != last; ++src, ++dest)
+ move(alloc, src, dest);
+ }
+};
+
+} // namespace container_internal
+} // namespace absl
+
+#endif // S2_THIRD_PARTY_ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_
--- /dev/null
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// MOTIVATION AND TUTORIAL
+//
+// If you want to put in a single heap allocation N doubles followed by M ints,
+// it's easy if N and M are known at compile time.
+//
+// struct S {
+// double a[N];
+// int b[M];
+// };
+//
+// S* p = new S;
+//
+// But what if N and M are known only in run time? Class template Layout to the
+// rescue! It's a portable generalization of the technique known as struct hack.
+//
+// // This object will tell us everything we need to know about the memory
+// // layout of double[N] followed by int[M]. It's structurally identical to
+// // size_t[2] that stores N and M. It's very cheap to create.
+// const Layout<double, int> layout(N, M);
+//
+// // Allocate enough memory for both arrays. `AllocSize()` tells us how much
+// // memory is needed. We are free to use any allocation function we want as
+// // long as it returns aligned memory.
+// std::unique_ptr<unsigned char[]> p(new unsigned char[layout.AllocSize()]);
+//
+// // Obtain the pointer to the array of doubles.
+// // Equivalent to `reinterpret_cast<double*>(p.get())`.
+// //
+// // We could have written layout.Pointer<0>(p) instead. If all the types are
+// // unique you can use either form, but if some types are repeated you must
+// // use the index form.
+// double* a = layout.Pointer<double>(p.get());
+//
+// // Obtain the pointer to the array of ints.
+// // Equivalent to `reinterpret_cast<int*>(p.get() + N * 8)`.
+// int* b = layout.Pointer<int>(p);
+//
+// If we are unable to specify sizes of all fields, we can pass as many sizes as
+// we can to `Partial()`. In return, it'll allow us to access the fields whose
+// locations and sizes can be computed from the provided information.
+// `Partial()` comes in handy when the array sizes are embedded into the
+// allocation.
+//
+// // size_t[1] containing N, size_t[1] containing M, double[N], int[M].
+// using L = Layout<size_t, size_t, double, int>;
+//
+// unsigned char* Allocate(size_t n, size_t m) {
+// const L layout(1, 1, n, m);
+// unsigned char* p = new unsigned char[layout.AllocSize()];
+// *layout.Pointer<0>(p) = n;
+// *layout.Pointer<1>(p) = m;
+// return p;
+// }
+//
+// void Use(unsigned char* p) {
+// // First, extract N and M.
+// // Specify that the first array has only one element. Using `prefix` we
+// // can access the first two arrays but not more.
+// constexpr auto prefix = L::Partial(1);
+// size_t n = *prefix.Pointer<0>(p);
+// size_t m = *prefix.Pointer<1>(p);
+//
+// // Now we can get pointers to the payload.
+// const L layout(1, 1, n, m);
+// double* a = layout.Pointer<double>(p);
+// int* b = layout.Pointer<int>(p);
+// }
+//
+// The layout we used above combines fixed-size with dynamically-sized fields.
+// This is quite common. Layout is optimized for this use case and generates
+// optimal code. All computations that can be performed at compile time are
+// indeed performed at compile time.
+//
+// Efficiency tip: The order of fields matters. In `Layout<T1, ..., TN>` try to
+// ensure that `alignof(T1) >= ... >= alignof(TN)`. This way you'll have no
+// padding in between arrays.
+//
+// You can manually override the alignment of an array by wrapping the type in
+// `Aligned<T, N>`. `Layout<..., Aligned<T, N>, ...>` has exactly the same API
+// and behavior as `Layout<..., T, ...>` except that the first element of the
+// array of `T` is aligned to `N` (the rest of the elements follow without
+// padding). `N` cannot be less than `alignof(T)`.
+//
+// `AllocSize()` and `Pointer()` are the most basic methods for dealing with
+// memory layouts. Check out the reference or code below to discover more.
+//
+// EXAMPLE
+//
+// // Immutable move-only string with sizeof equal to sizeof(void*). The
+// // string size and the characters are kept in the same heap allocation.
+// class CompactString {
+// public:
+// CompactString(const char* s = "") {
+// const size_t size = strlen(s);
+// // size_t[1] followed by char[size + 1].
+// const L layout(1, size + 1);
+// p_.reset(new unsigned char[layout.AllocSize()]);
+// // If running under ASAN, mark the padding bytes, if any, to catch
+// // memory errors.
+// layout.PoisonPadding(p_.get());
+// // Store the size in the allocation.
+// *layout.Pointer<size_t>(p_.get()) = size;
+// // Store the characters in the allocation.
+// memcpy(layout.Pointer<char>(p_.get()), s, size + 1);
+// }
+//
+// size_t size() const {
+// // Equivalent to reinterpret_cast<size_t&>(*p).
+// return *L::Partial().Pointer<size_t>(p_.get());
+// }
+//
+// const char* c_str() const {
+// // Equivalent to reinterpret_cast<char*>(p.get() + sizeof(size_t)).
+// // The argument in Partial(1) specifies that we have size_t[1] in front
+// // of the characters.
+// return L::Partial(1).Pointer<char>(p_.get());
+// }
+//
+// private:
+// // Our heap allocation contains a size_t followed by an array of chars.
+// using L = Layout<size_t, char>;
+// std::unique_ptr<unsigned char[]> p_;
+// };
+//
+// int main() {
+// CompactString s = "hello";
+// assert(s.size() == 5);
+// assert(strcmp(s.c_str(), "hello") == 0);
+// }
+//
+// DOCUMENTATION
+//
+// The interface exported by this file consists of:
+// - class `Layout<>` and its public members.
+// - The public members of class `internal_layout::LayoutImpl<>`. That class
+// isn't intended to be used directly, and its name and template parameter
+// list are internal implementation details, but the class itself provides
+// most of the functionality in this file. See comments on its members for
+// detailed documentation.
+//
+// `Layout<T1,... Tn>::Partial(count1,..., countm)` (where `m` <= `n`) returns a
+// `LayoutImpl<>` object. `Layout<T1,..., Tn> layout(count1,..., countn)`
+// creates a `Layout` object, which exposes the same functionality by inheriting
+// from `LayoutImpl<>`.
+
+#ifndef S2_THIRD_PARTY_ABSL_CONTAINER_INTERNAL_LAYOUT_H_
+#define S2_THIRD_PARTY_ABSL_CONTAINER_INTERNAL_LAYOUT_H_
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <ostream>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <typeinfo>
+#include <utility>
+
+#ifdef ADDRESS_SANITIZER
+#include <sanitizer/asan_interface.h>
+#endif
+
+#include "s2/third_party/absl/meta/type_traits.h"
+#include "s2/third_party/absl/strings/str_cat.h"
+#include "s2/third_party/absl/types/span.h"
+#include "s2/third_party/absl/utility/utility.h"
+
+#if defined(__GXX_RTTI)
+#define ABSL_INTERNAL_HAS_CXA_DEMANGLE
+#endif
+
+#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE
+#include <cxxabi.h>
+#endif
+
+namespace absl {
+namespace container_internal {
+
+// A type wrapper that instructs `Layout` to use the specific alignment for the
+// array. `Layout<..., Aligned<T, N>, ...>` has exactly the same API
+// and behavior as `Layout<..., T, ...>` except that the first element of the
+// array of `T` is aligned to `N` (the rest of the elements follow without
+// padding).
+//
+// Requires: `N >= alignof(T)` and `N` is a power of 2.
+template <class T, size_t N>
+struct Aligned;
+
+namespace internal_layout {
+
+template <class T>
+struct NotAligned {};
+
+template <class T, size_t N>
+struct NotAligned<const Aligned<T, N>> {
+ static_assert(sizeof(T) == 0, "Aligned<T, N> cannot be const-qualified");
+};
+
+template <size_t>
+using IntToSize = size_t;
+
+template <class>
+using TypeToSize = size_t;
+
+template <class T>
+struct Type : NotAligned<T> {
+ using type = T;
+};
+
+template <class T, size_t N>
+struct Type<Aligned<T, N>> {
+ using type = T;
+};
+
+template <class T>
+struct SizeOf : NotAligned<T>, std::integral_constant<size_t, sizeof(T)> {};
+
+template <class T, size_t N>
+struct SizeOf<Aligned<T, N>> : std::integral_constant<size_t, sizeof(T)> {};
+
+// Note: workaround for https://gcc.gnu.org/PR88115
+template <class T>
+struct AlignOf : NotAligned<T> {
+ static constexpr size_t value = alignof(T);
+};
+
+template <class T, size_t N>
+struct AlignOf<Aligned<T, N>> {
+ static_assert(N % alignof(T) == 0,
+ "Custom alignment can't be lower than the type's alignment");
+ static constexpr size_t value = N;
+};
+
+// Does `Ts...` contain `T`?
+template <class T, class... Ts>
+using Contains = absl::disjunction<std::is_same<T, Ts>...>;
+
+template <class From, class To>
+using CopyConst =
+ typename std::conditional<std::is_const<From>::value, const To, To>::type;
+
+// Note: We're not qualifying this with absl:: because it doesn't compile under
+// MSVC.
+template <class T>
+using SliceType = Span<T>;
+
+// This namespace contains no types. It prevents functions defined in it from
+// being found by ADL.
+namespace adl_barrier {
+
+template <class Needle, class... Ts>
+constexpr size_t Find(Needle, Needle, Ts...) {
+ static_assert(!Contains<Needle, Ts...>(), "Duplicate element type");
+ return 0;
+}
+
+template <class Needle, class T, class... Ts>
+constexpr size_t Find(Needle, T, Ts...) {
+ return adl_barrier::Find(Needle(), Ts()...) + 1;
+}
+
+constexpr bool IsPow2(size_t n) { return !(n & (n - 1)); }
+
+// Returns `q * m` for the smallest `q` such that `q * m >= n`.
+// Requires: `m` is a power of two. It's enforced by IsLegalElementType below.
+constexpr size_t Align(size_t n, size_t m) { return (n + m - 1) & ~(m - 1); }
+
+constexpr size_t Min(size_t a, size_t b) { return b < a ? b : a; }
+
+constexpr size_t Max(size_t a) { return a; }
+
+template <class... Ts>
+constexpr size_t Max(size_t a, size_t b, Ts... rest) {
+ return adl_barrier::Max(b < a ? a : b, rest...);
+}
+
+template <class T>
+string TypeName() {
+ string out;
+ int status = 0;
+ char* demangled = nullptr;
+#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE
+ demangled = abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status);
+#endif
+ if (status == 0 && demangled != nullptr) { // Demangling succeeded.
+ absl::StrAppend(&out, "<", demangled, ">");
+ free(demangled);
+ } else {
+#if defined(__GXX_RTTI) || defined(_CPPRTTI)
+ absl::StrAppend(&out, "<", typeid(T).name(), ">");
+#endif
+ }
+ return out;
+}
+
+} // namespace adl_barrier
+
+template <bool C>
+using EnableIf = typename std::enable_if<C, int>::type;
+
+// Can `T` be a template argument of `Layout`?
+template <class T>
+using IsLegalElementType = std::integral_constant<
+ bool, !std::is_reference<T>::value && !std::is_volatile<T>::value &&
+ !std::is_reference<typename Type<T>::type>::value &&
+ !std::is_volatile<typename Type<T>::type>::value &&
+ adl_barrier::IsPow2(AlignOf<T>::value)>;
+
+template <class Elements, class SizeSeq, class OffsetSeq>
+class LayoutImpl;
+
+// Public base class of `Layout` and the result type of `Layout::Partial()`.
+//
+// `Elements...` contains all template arguments of `Layout` that created this
+// instance.
+//
+// `SizeSeq...` is `[0, NumSizes)` where `NumSizes` is the number of arguments
+// passed to `Layout::Partial()` or `Layout::Layout()`.
+//
+// `OffsetSeq...` is `[0, NumOffsets)` where `NumOffsets` is
+// `Min(sizeof...(Elements), NumSizes + 1)` (the number of arrays for which we
+// can compute offsets).
+template <class... Elements, size_t... SizeSeq, size_t... OffsetSeq>
+class LayoutImpl<std::tuple<Elements...>, absl::index_sequence<SizeSeq...>,
+ absl::index_sequence<OffsetSeq...>> {
+ private:
+ static_assert(sizeof...(Elements) > 0, "At least one field is required");
+ static_assert(absl::conjunction<IsLegalElementType<Elements>...>::value,
+ "Invalid element type (see IsLegalElementType)");
+
+ enum {
+ NumTypes = sizeof...(Elements),
+ NumSizes = sizeof...(SizeSeq),
+ NumOffsets = sizeof...(OffsetSeq),
+ };
+
+ // These are guaranteed by `Layout`.
+ static_assert(NumOffsets == adl_barrier::Min(NumTypes, NumSizes + 1),
+ "Internal error");
+ static_assert(NumTypes > 0, "Internal error");
+
+ // Returns the index of `T` in `Elements...`. Results in a compilation error
+ // if `Elements...` doesn't contain exactly one instance of `T`.
+ template <class T>
+ static constexpr size_t ElementIndex() {
+ static_assert(Contains<Type<T>, Type<typename Type<Elements>::type>...>(),
+ "Type not found");
+ return adl_barrier::Find(Type<T>(),
+ Type<typename Type<Elements>::type>()...);
+ }
+
+ template <size_t N>
+ using ElementAlignment =
+ AlignOf<typename std::tuple_element<N, std::tuple<Elements...>>::type>;
+
+ public:
+ // Element types of all arrays packed in a tuple.
+ using ElementTypes = std::tuple<typename Type<Elements>::type...>;
+
+ // Element type of the Nth array.
+ template <size_t N>
+ using ElementType = typename std::tuple_element<N, ElementTypes>::type;
+
+ constexpr explicit LayoutImpl(IntToSize<SizeSeq>... sizes)
+ : size_{sizes...} {}
+
+ // Alignment of the layout, equal to the strictest alignment of all elements.
+ // All pointers passed to the methods of layout must be aligned to this value.
+ static constexpr size_t Alignment() {
+ return adl_barrier::Max(AlignOf<Elements>::value...);
+ }
+
+ // Offset in bytes of the Nth array.
+ //
+ // // int[3], 4 bytes of padding, double[4].
+ // Layout<int, double> x(3, 4);
+ // assert(x.Offset<0>() == 0); // The ints starts from 0.
+ // assert(x.Offset<1>() == 16); // The doubles starts from 16.
+ //
+ // Requires: `N <= NumSizes && N < sizeof...(Ts)`.
+ template <size_t N, EnableIf<N == 0> = 0>
+ constexpr size_t Offset() const {
+ return 0;
+ }
+
+ template <size_t N, EnableIf<N != 0> = 0>
+ constexpr size_t Offset() const {
+ static_assert(N < NumOffsets, "Index out of bounds");
+ return adl_barrier::Align(
+ Offset<N - 1>() + SizeOf<ElementType<N - 1>>() * size_[N - 1],
+ ElementAlignment<N>::value);
+ }
+
+ // Offset in bytes of the array with the specified element type. There must
+ // be exactly one such array and its zero-based index must be at most
+ // `NumSizes`.
+ //
+ // // int[3], 4 bytes of padding, double[4].
+ // Layout<int, double> x(3, 4);
+ // assert(x.Offset<int>() == 0); // The ints starts from 0.
+ // assert(x.Offset<double>() == 16); // The doubles starts from 16.
+ template <class T>
+ constexpr size_t Offset() const {
+ return Offset<ElementIndex<T>()>();
+ }
+
+ // Offsets in bytes of all arrays for which the offsets are known.
+ constexpr std::array<size_t, NumOffsets> Offsets() const {
+ return {{Offset<OffsetSeq>()...}};
+ }
+
+ // The number of elements in the Nth array. This is the Nth argument of
+ // `Layout::Partial()` or `Layout::Layout()` (zero-based).
+ //
+ // // int[3], 4 bytes of padding, double[4].
+ // Layout<int, double> x(3, 4);
+ // assert(x.Size<0>() == 3);
+ // assert(x.Size<1>() == 4);
+ //
+ // Requires: `N < NumSizes`.
+ template <size_t N>
+ constexpr size_t Size() const {
+ static_assert(N < NumSizes, "Index out of bounds");
+ return size_[N];
+ }
+
+ // The number of elements in the array with the specified element type.
+ // There must be exactly one such array and its zero-based index must be
+ // at most `NumSizes`.
+ //
+ // // int[3], 4 bytes of padding, double[4].
+ // Layout<int, double> x(3, 4);
+ // assert(x.Size<int>() == 3);
+ // assert(x.Size<double>() == 4);
+ template <class T>
+ constexpr size_t Size() const {
+ return Size<ElementIndex<T>()>();
+ }
+
+ // The number of elements of all arrays for which they are known.
+ constexpr std::array<size_t, NumSizes> Sizes() const {
+ return {{Size<SizeSeq>()...}};
+ }
+
+ // Pointer to the beginning of the Nth array.
+ //
+ // `Char` must be `[const] [signed|unsigned] char`.
+ //
+ // // int[3], 4 bytes of padding, double[4].
+ // Layout<int, double> x(3, 4);
+ // unsigned char* p = new unsigned char[x.AllocSize()];
+ // int* ints = x.Pointer<0>(p);
+ // double* doubles = x.Pointer<1>(p);
+ //
+ // Requires: `N <= NumSizes && N < sizeof...(Ts)`.
+ // Requires: `p` is aligned to `Alignment()`.
+ template <size_t N, class Char>
+ CopyConst<Char, ElementType<N>>* Pointer(Char* p) const {
+ using C = typename std::remove_const<Char>::type;
+ static_assert(
+ std::is_same<C, char>() || std::is_same<C, unsigned char>() ||
+ std::is_same<C, signed char>(),
+ "The argument must be a pointer to [const] [signed|unsigned] char");
+ // Use a named variable to work around b/33479323.
+ constexpr size_t alignment = Alignment();
+ (void)alignment;
+ assert(reinterpret_cast<uintptr_t>(p) % alignment == 0);
+ return reinterpret_cast<CopyConst<Char, ElementType<N>>*>(p + Offset<N>());
+ }
+
+ // Pointer to the beginning of the array with the specified element type.
+ // There must be exactly one such array and its zero-based index must be at
+ // most `NumSizes`.
+ //
+ // `Char` must be `[const] [signed|unsigned] char`.
+ //
+ // // int[3], 4 bytes of padding, double[4].
+ // Layout<int, double> x(3, 4);
+ // unsigned char* p = new unsigned char[x.AllocSize()];
+ // int* ints = x.Pointer<int>(p);
+ // double* doubles = x.Pointer<double>(p);
+ //
+ // Requires: `p` is aligned to `Alignment()`.
+ template <class T, class Char>
+ CopyConst<Char, T>* Pointer(Char* p) const {
+ return Pointer<ElementIndex<T>()>(p);
+ }
+
+ // Pointers to all arrays for which pointers are known.
+ //
+ // `Char` must be `[const] [signed|unsigned] char`.
+ //
+ // // int[3], 4 bytes of padding, double[4].
+ // Layout<int, double> x(3, 4);
+ // unsigned char* p = new unsigned char[x.AllocSize()];
+ //
+ // int* ints;
+ // double* doubles;
+ // std::tie(ints, doubles) = x.Pointers(p);
+ //
+ // Requires: `p` is aligned to `Alignment()`.
+ //
+ // Note: We're not using ElementType alias here because it does not compile
+ // under MSVC.
+ template <class Char>
+ std::tuple<CopyConst<
+ Char, typename std::tuple_element<OffsetSeq, ElementTypes>::type>*...>
+ Pointers(Char* p) const {
+ return std::tuple<CopyConst<Char, ElementType<OffsetSeq>>*...>(
+ Pointer<OffsetSeq>(p)...);
+ }
+
+ // The Nth array.
+ //
+ // `Char` must be `[const] [signed|unsigned] char`.
+ //
+ // // int[3], 4 bytes of padding, double[4].
+ // Layout<int, double> x(3, 4);
+ // unsigned char* p = new unsigned char[x.AllocSize()];
+ // Span<int> ints = x.Slice<0>(p);
+ // Span<double> doubles = x.Slice<1>(p);
+ //
+ // Requires: `N < NumSizes`.
+ // Requires: `p` is aligned to `Alignment()`.
+ template <size_t N, class Char>
+ SliceType<CopyConst<Char, ElementType<N>>> Slice(Char* p) const {
+ return SliceType<CopyConst<Char, ElementType<N>>>(Pointer<N>(p), Size<N>());
+ }
+
+ // The array with the specified element type. There must be exactly one
+ // such array and its zero-based index must be less than `NumSizes`.
+ //
+ // `Char` must be `[const] [signed|unsigned] char`.
+ //
+ // // int[3], 4 bytes of padding, double[4].
+ // Layout<int, double> x(3, 4);
+ // unsigned char* p = new unsigned char[x.AllocSize()];
+ // Span<int> ints = x.Slice<int>(p);
+ // Span<double> doubles = x.Slice<double>(p);
+ //
+ // Requires: `p` is aligned to `Alignment()`.
+ template <class T, class Char>
+ SliceType<CopyConst<Char, T>> Slice(Char* p) const {
+ return Slice<ElementIndex<T>()>(p);
+ }
+
+ // All arrays with known sizes.
+ //
+ // `Char` must be `[const] [signed|unsigned] char`.
+ //
+ // // int[3], 4 bytes of padding, double[4].
+ // Layout<int, double> x(3, 4);
+ // unsigned char* p = new unsigned char[x.AllocSize()];
+ //
+ // Span<int> ints;
+ // Span<double> doubles;
+ // std::tie(ints, doubles) = x.Slices(p);
+ //
+ // Requires: `p` is aligned to `Alignment()`.
+ //
+ // Note: We're not using ElementType alias here because it does not compile
+ // under MSVC.
+ template <class Char>
+ std::tuple<SliceType<CopyConst<
+ Char, typename std::tuple_element<SizeSeq, ElementTypes>::type>>...>
+ Slices(Char* p) const {
+ // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63875 (fixed
+ // in 6.1).
+ (void)p;
+ return std::tuple<SliceType<CopyConst<Char, ElementType<SizeSeq>>>...>(
+ Slice<SizeSeq>(p)...);
+ }
+
+ // The size of the allocation that fits all arrays.
+ //
+ // // int[3], 4 bytes of padding, double[4].
+ // Layout<int, double> x(3, 4);
+ // unsigned char* p = new unsigned char[x.AllocSize()]; // 48 bytes
+ //
+ // Requires: `NumSizes == sizeof...(Ts)`.
+ constexpr size_t AllocSize() const {
+ static_assert(NumTypes == NumSizes, "You must specify sizes of all fields");
+ return Offset<NumTypes - 1>() +
+ SizeOf<ElementType<NumTypes - 1>>() * size_[NumTypes - 1];
+ }
+
+ // If built with --config=asan, poisons padding bytes (if any) in the
+ // allocation. The pointer must point to a memory block at least
+ // `AllocSize()` bytes in length.
+ //
+ // `Char` must be `[const] [signed|unsigned] char`.
+ //
+ // Requires: `p` is aligned to `Alignment()`.
+ template <class Char, size_t N = NumOffsets - 1, EnableIf<N == 0> = 0>
+ void PoisonPadding(const Char* p) const {
+ Pointer<0>(p); // verify the requirements on `Char` and `p`
+ }
+
+ template <class Char, size_t N = NumOffsets - 1, EnableIf<N != 0> = 0>
+ void PoisonPadding(const Char* p) const {
+ static_assert(N < NumOffsets, "Index out of bounds");
+ (void)p;
+#ifdef ADDRESS_SANITIZER
+ PoisonPadding<Char, N - 1>(p);
+ // The `if` is an optimization. It doesn't affect the observable behaviour.
+ if (ElementAlignment<N - 1>::value % ElementAlignment<N>::value) {
+ size_t start =
+ Offset<N - 1>() + SizeOf<ElementType<N - 1>>() * size_[N - 1];
+ ASAN_POISON_MEMORY_REGION(p + start, Offset<N>() - start);
+ }
+#endif
+ }
+
+ // Human-readable description of the memory layout. Useful for debugging.
+ // Slow.
+ //
+ // // char[5], 3 bytes of padding, int[3], 4 bytes of padding, followed
+ // // by an unknown number of doubles.
+ // auto x = Layout<char, int, double>::Partial(5, 3);
+ // assert(x.DebugString() ==
+ // "@0<char>(1)[5]; @8<int>(4)[3]; @24<double>(8)");
+ //
+ // Each field is in the following format: @offset<type>(sizeof)[size] (<type>
+ // may be missing depending on the target platform). For example,
+ // @8<int>(4)[3] means that at offset 8 we have an array of ints, where each
+ // int is 4 bytes, and we have 3 of those ints. The size of the last field may
+ // be missing (as in the example above). Only fields with known offsets are
+ // described. Type names may differ across platforms: one compiler might
+ // produce "unsigned*" where another produces "unsigned int *".
+ string DebugString() const {
+ const auto offsets = Offsets();
+ const size_t sizes[] = {SizeOf<ElementType<OffsetSeq>>()...};
+ const string types[] = {adl_barrier::TypeName<ElementType<OffsetSeq>>()...};
+ string res = absl::StrCat("@0", types[0], "(", sizes[0], ")");
+ for (size_t i = 0; i != NumOffsets - 1; ++i) {
+ absl::StrAppend(&res, "[", size_[i], "]; @", offsets[i + 1], types[i + 1],
+ "(", sizes[i + 1], ")");
+ }
+ // NumSizes is a constant that may be zero. Some compilers cannot see that
+ // inside the if statement "size_[NumSizes - 1]" must be valid.
+ int last = static_cast<int>(NumSizes) - 1;
+ if (NumTypes == NumSizes && last >= 0) {
+ absl::StrAppend(&res, "[", size_[last], "]");
+ }
+ return res;
+ }
+
+ private:
+ // Arguments of `Layout::Partial()` or `Layout::Layout()`.
+ size_t size_[NumSizes > 0 ? NumSizes : 1];
+};
+
+template <size_t NumSizes, class... Ts>
+using LayoutType = LayoutImpl<
+ std::tuple<Ts...>, absl::make_index_sequence<NumSizes>,
+ absl::make_index_sequence<adl_barrier::Min(sizeof...(Ts), NumSizes + 1)>>;
+
+} // namespace internal_layout
+
+// Descriptor of arrays of various types and sizes laid out in memory one after
+// another. See the top of the file for documentation.
+//
+// Check out the public API of internal_layout::LayoutImpl above. The type is
+// internal to the library but its methods are public, and they are inherited
+// by `Layout`.
+template <class... Ts>
+class Layout : public internal_layout::LayoutType<sizeof...(Ts), Ts...> {
+ public:
+ static_assert(sizeof...(Ts) > 0, "At least one field is required");
+ static_assert(
+ absl::conjunction<internal_layout::IsLegalElementType<Ts>...>::value,
+ "Invalid element type (see IsLegalElementType)");
+
+ // The result type of `Partial()` with `NumSizes` arguments.
+ template <size_t NumSizes>
+ using PartialType = internal_layout::LayoutType<NumSizes, Ts...>;
+
+ // `Layout` knows the element types of the arrays we want to lay out in
+ // memory but not the number of elements in each array.
+ // `Partial(size1, ..., sizeN)` allows us to specify the latter. The
+ // resulting immutable object can be used to obtain pointers to the
+ // individual arrays.
+ //
+ // It's allowed to pass fewer array sizes than the number of arrays. E.g.,
+ // if all you need is to the offset of the second array, you only need to
+ // pass one argument -- the number of elements in the first array.
+ //
+ // // int[3] followed by 4 bytes of padding and an unknown number of
+ // // doubles.
+ // auto x = Layout<int, double>::Partial(3);
+ // // doubles start at byte 16.
+ // assert(x.Offset<1>() == 16);
+ //
+ // If you know the number of elements in all arrays, you can still call
+ // `Partial()` but it's more convenient to use the constructor of `Layout`.
+ //
+ // Layout<int, double> x(3, 5);
+ //
+ // Note: The sizes of the arrays must be specified in number of elements,
+ // not in bytes.
+ //
+ // Requires: `sizeof...(Sizes) <= sizeof...(Ts)`.
+ // Requires: all arguments are convertible to `size_t`.
+ template <class... Sizes>
+ static constexpr PartialType<sizeof...(Sizes)> Partial(Sizes&&... sizes) {
+ static_assert(sizeof...(Sizes) <= sizeof...(Ts), "");
+ return PartialType<sizeof...(Sizes)>(absl::forward<Sizes>(sizes)...);
+ }
+
+ // Creates a layout with the sizes of all arrays specified. If you know
+ // only the sizes of the first N arrays (where N can be zero), you can use
+ // `Partial()` defined above. The constructor is essentially equivalent to
+ // calling `Partial()` and passing in all array sizes; the constructor is
+ // provided as a convenient abbreviation.
+ //
+ // Note: The sizes of the arrays must be specified in number of elements,
+ // not in bytes.
+ constexpr explicit Layout(internal_layout::TypeToSize<Ts>... sizes)
+ : internal_layout::LayoutType<sizeof...(Ts), Ts...>(sizes...) {}
+};
+
+} // namespace container_internal
+} // namespace absl
+
+#endif // S2_THIRD_PARTY_ABSL_CONTAINER_INTERNAL_LAYOUT_H_
--- /dev/null
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: memory.h
+// -----------------------------------------------------------------------------
+//
+// This header file contains utility functions for managing the creation and
+// conversion of smart pointers. This file is an extension to the C++
+// standard <memory> library header file.
+
+#ifndef S2_THIRD_PARTY_ABSL_MEMORY_MEMORY_H_
+#define S2_THIRD_PARTY_ABSL_MEMORY_MEMORY_H_
+
+#include <cstddef>
+#include <limits>
+#include <memory>
+#include <new>
+#include <type_traits>
+#include <utility>
+
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/third_party/absl/meta/type_traits.h"
+
+namespace absl {
+
+// -----------------------------------------------------------------------------
+// Function Template: WrapUnique()
+// -----------------------------------------------------------------------------
+//
+// Adopts ownership from a raw pointer and transfers it to the returned
+// `std::unique_ptr`, whose type is deduced. Because of this deduction, *do not*
+// specify the template type `T` when calling `WrapUnique`.
+//
+// Example:
+// X* NewX(int, int);
+// auto x = WrapUnique(NewX(1, 2)); // 'x' is std::unique_ptr<X>.
+//
+// The purpose of WrapUnique is to automatically deduce the pointer type. If you
+// wish to make the type explicit, for readability reasons or because you prefer
+// to use a base-class pointer rather than a derived one, just use
+// `std::unique_ptr` directly.
+//
+// Example:
+// X* Factory(int, int);
+// auto x = std::unique_ptr<X>(Factory(1, 2));
+// - or -
+// std::unique_ptr<X> x(Factory(1, 2));
+//
+// This has the added advantage of working whether Factory returns a raw
+// pointer or a `std::unique_ptr`.
+//
+// While `absl::WrapUnique` is useful for capturing the output of a raw
+// pointer factory, prefer 'absl::make_unique<T>(args...)' over
+// 'absl::WrapUnique(new T(args...))'.
+//
+// auto x = WrapUnique(new X(1, 2)); // works, but nonideal.
+// auto x = make_unique<X>(1, 2); // safer, standard, avoids raw 'new'.
+//
+// Note that `absl::WrapUnique(p)` is valid only if `delete p` is a valid
+// expression. In particular, `absl::WrapUnique()` cannot wrap pointers to
+// arrays, functions or void, and it must not be used to capture pointers
+// obtained from array-new expressions (even though that would compile!).
+template <typename T>
+std::unique_ptr<T> WrapUnique(T* ptr) {
+ static_assert(!std::is_array<T>::value, "array types are unsupported");
+ static_assert(std::is_object<T>::value, "non-object types are unsupported");
+ return std::unique_ptr<T>(ptr);
+}
+
+namespace memory_internal {
+
+// Traits to select proper overload and return type for `absl::make_unique<>`.
+template <typename T>
+struct MakeUniqueResult {
+ using scalar = std::unique_ptr<T>;
+};
+template <typename T>
+struct MakeUniqueResult<T[]> {
+ using array = std::unique_ptr<T[]>;
+};
+template <typename T, size_t N>
+struct MakeUniqueResult<T[N]> {
+ using invalid = void;
+};
+
+} // namespace memory_internal
+
+// gcc 4.8 has __cplusplus at 201301 but doesn't define make_unique. Other
+// supported compilers either just define __cplusplus as 201103 but have
+// make_unique (msvc), or have make_unique whenever __cplusplus > 201103 (clang)
+#if (__cplusplus > 201103L || defined(_MSC_VER)) && \
+ !(defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 8)
+using std::make_unique;
+#else
+// -----------------------------------------------------------------------------
+// Function Template: make_unique<T>()
+// -----------------------------------------------------------------------------
+//
+// Creates a `std::unique_ptr<>`, while avoiding issues creating temporaries
+// during the construction process. `absl::make_unique<>` also avoids redundant
+// type declarations, by avoiding the need to explicitly use the `new` operator.
+//
+// This implementation of `absl::make_unique<>` is designed for C++11 code and
+// will be replaced in C++14 by the equivalent `std::make_unique<>` abstraction.
+// `absl::make_unique<>` is designed to be 100% compatible with
+// `std::make_unique<>` so that the eventual migration will involve a simple
+// rename operation.
+//
+// For more background on why `std::unique_ptr<T>(new T(a,b))` is problematic,
+// see Herb Sutter's explanation on
+// (Exception-Safe Function Calls)[http://herbsutter.com/gotw/_102/].
+// (In general, reviewers should treat `new T(a,b)` with scrutiny.)
+//
+// Example usage:
+//
+// auto p = make_unique<X>(args...); // 'p' is a std::unique_ptr<X>
+// auto pa = make_unique<X[]>(5); // 'pa' is a std::unique_ptr<X[]>
+//
+// Three overloads of `absl::make_unique` are required:
+//
+// - For non-array T:
+//
+// Allocates a T with `new T(std::forward<Args> args...)`,
+// forwarding all `args` to T's constructor.
+// Returns a `std::unique_ptr<T>` owning that object.
+//
+// - For an array of unknown bounds T[]:
+//
+// `absl::make_unique<>` will allocate an array T of type U[] with
+// `new U[n]()` and return a `std::unique_ptr<U[]>` owning that array.
+//
+// Note that 'U[n]()' is different from 'U[n]', and elements will be
+// value-initialized. Note as well that `std::unique_ptr` will perform its
+// own destruction of the array elements upon leaving scope, even though
+// the array [] does not have a default destructor.
+//
+// NOTE: an array of unknown bounds T[] may still be (and often will be)
+// initialized to have a size, and will still use this overload. E.g:
+//
+// auto my_array = absl::make_unique<int[]>(10);
+//
+// - For an array of known bounds T[N]:
+//
+// `absl::make_unique<>` is deleted (like with `std::make_unique<>`) as
+// this overload is not useful.
+//
+// NOTE: an array of known bounds T[N] is not considered a useful
+// construction, and may cause undefined behavior in templates. E.g:
+//
+// auto my_array = absl::make_unique<int[10]>();
+//
+// In those cases, of course, you can still use the overload above and
+// simply initialize it to its desired size:
+//
+// auto my_array = absl::make_unique<int[]>(10);
+
+// `absl::make_unique` overload for non-array types.
+template <typename T, typename... Args>
+typename memory_internal::MakeUniqueResult<T>::scalar make_unique(
+ Args&&... args) {
+ return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+}
+
+// `absl::make_unique` overload for an array T[] of unknown bounds.
+// The array allocation needs to use the `new T[size]` form and cannot take
+// element constructor arguments. The `std::unique_ptr` will manage destructing
+// these array elements.
+template <typename T>
+typename memory_internal::MakeUniqueResult<T>::array make_unique(size_t n) {
+ return std::unique_ptr<T>(new typename absl::remove_extent_t<T>[n]());
+}
+
+// `absl::make_unique` overload for an array T[N] of known bounds.
+// This construction will be rejected.
+template <typename T, typename... Args>
+typename memory_internal::MakeUniqueResult<T>::invalid make_unique(
+ Args&&... /* args */) = delete;
+#endif
+
+
+// These are make_unique_default_init variants modeled after
+// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1020r0.html
+// Unlike absl::make_unique, values are default initialized rather than value
+// initialized.
+//
+// `absl::make_unique_default_init` overload for non-array types.
+template <typename T>
+typename memory_internal::MakeUniqueResult<T>::scalar
+ make_unique_default_init() {
+ return std::unique_ptr<T>(new T);
+}
+
+// `absl::make_unique_default_init` overload for an array T[] of unknown bounds.
+// The array allocation needs to use the `new T[size]` form and cannot take
+// element constructor arguments. The `std::unique_ptr` will manage destructing
+// these array elements.
+template <typename T>
+typename memory_internal::MakeUniqueResult<T>::array
+ make_unique_default_init(size_t n) {
+ return std::unique_ptr<T>(new typename absl::remove_extent_t<T>[n]);
+}
+
+// `absl::make_unique_default_init` overload for an array T[N] of known bounds.
+// This construction will be rejected.
+template <typename T, typename... Args>
+typename memory_internal::MakeUniqueResult<T>::invalid
+ make_unique_default_init(Args&&... /* args */) = delete;
+
+
+
+// NOTE TO GOOGLERS:
+// absl::MakeUnique / gtl::MakeUnique is a legacy spelling. New code should
+// prefer absl::make_unique.
+
+// `absl::MakeUnique` overload for non-array types.
+template <typename T, typename... Args>
+typename memory_internal::MakeUniqueResult<T>::scalar
+ MakeUnique(Args&&... args) {
+ return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+}
+
+// `absl::MakeUnique` overload for an array T[] of unknown bounds.
+// The array allocation needs to use the `new T[size]` form and cannot take
+// element constructor arguments. The `std::unique_ptr` will manage destructing
+// these array elements.
+template <typename T>
+typename memory_internal::MakeUniqueResult<T>::array MakeUnique(size_t n) {
+ return std::unique_ptr<T>(new typename absl::remove_extent_t<T>[n]());
+}
+
+// `absl::MakeUnique` overload for an array T[N] of known bounds.
+// This construction will be rejected.
+template <typename T, typename... Args>
+typename memory_internal::MakeUniqueResult<T>::invalid
+ MakeUnique(Args&&... /* args */) = delete;
+
+// -----------------------------------------------------------------------------
+// Function Template: RawPtr()
+// -----------------------------------------------------------------------------
+//
+// Extracts the raw pointer from a pointer-like value `ptr`. `absl::RawPtr` is
+// useful within templates that need to handle a complement of raw pointers,
+// `std::nullptr_t`, and smart pointers.
+template <typename T>
+auto RawPtr(T&& ptr) -> decltype(std::addressof(*ptr)) {
+ // ptr is a forwarding reference to support Ts with non-const operators.
+ return (ptr != nullptr) ? std::addressof(*ptr) : nullptr;
+}
+inline std::nullptr_t RawPtr(std::nullptr_t) { return nullptr; }
+
+// -----------------------------------------------------------------------------
+// Function Template: ShareUniquePtr()
+// -----------------------------------------------------------------------------
+//
+// Adopts a `std::unique_ptr` rvalue and returns a `std::shared_ptr` of deduced
+// type. Ownership (if any) of the held value is transferred to the returned
+// shared pointer.
+//
+// Example:
+//
+// auto up = absl::make_unique<int>(10);
+// auto sp = absl::ShareUniquePtr(std::move(up)); // shared_ptr<int>
+// CHECK_EQ(*sp, 10);
+// CHECK(up == nullptr);
+//
+// Note that this conversion is correct even when T is an array type, and more
+// generally it works for *any* deleter of the `unique_ptr` (single-object
+// deleter, array deleter, or any custom deleter), since the deleter is adopted
+// by the shared pointer as well. The deleter is copied (unless it is a
+// reference).
+//
+// Implements the resolution of [LWG 2415](http://wg21.link/lwg2415), by which a
+// null shared pointer does not attempt to call the deleter.
+template <typename T, typename D>
+std::shared_ptr<T> ShareUniquePtr(std::unique_ptr<T, D>&& ptr) {
+ return ptr ? std::shared_ptr<T>(std::move(ptr)) : std::shared_ptr<T>();
+}
+
+// -----------------------------------------------------------------------------
+// Function Template: WeakenPtr()
+// -----------------------------------------------------------------------------
+//
+// Creates a weak pointer associated with a given shared pointer. The returned
+// value is a `std::weak_ptr` of deduced type.
+//
+// Example:
+//
+// auto sp = std::make_shared<int>(10);
+// auto wp = absl::WeakenPtr(sp);
+// CHECK_EQ(sp.get(), wp.lock().get());
+// sp.reset();
+// CHECK(wp.lock() == nullptr);
+//
+template <typename T>
+std::weak_ptr<T> WeakenPtr(const std::shared_ptr<T>& ptr) {
+ return std::weak_ptr<T>(ptr);
+}
+
+namespace memory_internal {
+
+// ExtractOr<E, O, D>::type evaluates to E<O> if possible. Otherwise, D.
+template <template <typename> class Extract, typename Obj, typename Default,
+ typename>
+struct ExtractOr {
+ using type = Default;
+};
+
+template <template <typename> class Extract, typename Obj, typename Default>
+struct ExtractOr<Extract, Obj, Default, void_t<Extract<Obj>>> {
+ using type = Extract<Obj>;
+};
+
+template <template <typename> class Extract, typename Obj, typename Default>
+using ExtractOrT = typename ExtractOr<Extract, Obj, Default, void>::type;
+
+// Extractors for the features of allocators.
+template <typename T>
+using GetPointer = typename T::pointer;
+
+template <typename T>
+using GetConstPointer = typename T::const_pointer;
+
+template <typename T>
+using GetVoidPointer = typename T::void_pointer;
+
+template <typename T>
+using GetConstVoidPointer = typename T::const_void_pointer;
+
+template <typename T>
+using GetDifferenceType = typename T::difference_type;
+
+template <typename T>
+using GetSizeType = typename T::size_type;
+
+template <typename T>
+using GetPropagateOnContainerCopyAssignment =
+ typename T::propagate_on_container_copy_assignment;
+
+template <typename T>
+using GetPropagateOnContainerMoveAssignment =
+ typename T::propagate_on_container_move_assignment;
+
+template <typename T>
+using GetPropagateOnContainerSwap = typename T::propagate_on_container_swap;
+
+template <typename T>
+using GetIsAlwaysEqual = typename T::is_always_equal;
+
+template <typename T>
+struct GetFirstArg;
+
+template <template <typename...> class Class, typename T, typename... Args>
+struct GetFirstArg<Class<T, Args...>> {
+ using type = T;
+};
+
+template <typename Ptr, typename = void>
+struct ElementType {
+ using type = typename GetFirstArg<Ptr>::type;
+};
+
+template <typename T>
+struct ElementType<T, void_t<typename T::element_type>> {
+ using type = typename T::element_type;
+};
+
+template <typename T, typename U>
+struct RebindFirstArg;
+
+template <template <typename...> class Class, typename T, typename... Args,
+ typename U>
+struct RebindFirstArg<Class<T, Args...>, U> {
+ using type = Class<U, Args...>;
+};
+
+template <typename T, typename U, typename = void>
+struct RebindPtr {
+ using type = typename RebindFirstArg<T, U>::type;
+};
+
+template <typename T, typename U>
+struct RebindPtr<T, U, void_t<typename T::template rebind<U>>> {
+ using type = typename T::template rebind<U>;
+};
+
+template <typename T, typename U>
+constexpr bool HasRebindAlloc(...) {
+ return false;
+}
+
+template <typename T, typename U>
+constexpr bool HasRebindAlloc(typename T::template rebind<U>::other*) {
+ return true;
+}
+
+template <typename T, typename U, bool = HasRebindAlloc<T, U>(nullptr)>
+struct RebindAlloc {
+ using type = typename RebindFirstArg<T, U>::type;
+};
+
+template <typename T, typename U>
+struct RebindAlloc<T, U, true> {
+ using type = typename T::template rebind<U>::other;
+};
+
+} // namespace memory_internal
+
+// -----------------------------------------------------------------------------
+// Class Template: pointer_traits
+// -----------------------------------------------------------------------------
+//
+// An implementation of C++11's std::pointer_traits.
+//
+// Provided for portability on toolchains that have a working C++11 compiler,
+// but the standard library is lacking in C++11 support. For example, some
+// version of the Android NDK.
+//
+
+template <typename Ptr>
+struct pointer_traits {
+ using pointer = Ptr;
+
+ // element_type:
+ // Ptr::element_type if present. Otherwise T if Ptr is a template
+ // instantiation Template<T, Args...>
+ using element_type = typename memory_internal::ElementType<Ptr>::type;
+
+ // difference_type:
+ // Ptr::difference_type if present, otherwise std::ptrdiff_t
+ using difference_type =
+ memory_internal::ExtractOrT<memory_internal::GetDifferenceType, Ptr,
+ std::ptrdiff_t>;
+
+ // rebind:
+ // Ptr::rebind<U> if exists, otherwise Template<U, Args...> if Ptr is a
+ // template instantiation Template<T, Args...>
+ template <typename U>
+ using rebind = typename memory_internal::RebindPtr<Ptr, U>::type;
+
+ // pointer_to:
+ // Calls Ptr::pointer_to(r)
+ static pointer pointer_to(element_type& r) { // NOLINT(runtime/references)
+ return Ptr::pointer_to(r);
+ }
+};
+
+// Specialization for T*.
+template <typename T>
+struct pointer_traits<T*> {
+ using pointer = T*;
+ using element_type = T;
+ using difference_type = std::ptrdiff_t;
+
+ template <typename U>
+ using rebind = U*;
+
+ // pointer_to:
+ // Calls std::addressof(r)
+ static pointer pointer_to(
+ element_type& r) noexcept { // NOLINT(runtime/references)
+ return std::addressof(r);
+ }
+};
+
+// -----------------------------------------------------------------------------
+// Class Template: allocator_traits
+// -----------------------------------------------------------------------------
+//
+// A C++11 compatible implementation of C++17's std::allocator_traits.
+//
+template <typename Alloc>
+struct allocator_traits {
+ using allocator_type = Alloc;
+
+ // value_type:
+ // Alloc::value_type
+ using value_type = typename Alloc::value_type;
+
+ // pointer:
+ // Alloc::pointer if present, otherwise value_type*
+ using pointer = memory_internal::ExtractOrT<memory_internal::GetPointer,
+ Alloc, value_type*>;
+
+ // const_pointer:
+ // Alloc::const_pointer if present, otherwise
+ // absl::pointer_traits<pointer>::rebind<const value_type>
+ using const_pointer =
+ memory_internal::ExtractOrT<memory_internal::GetConstPointer, Alloc,
+ typename absl::pointer_traits<pointer>::
+ template rebind<const value_type>>;
+
+ // void_pointer:
+ // Alloc::void_pointer if present, otherwise
+ // absl::pointer_traits<pointer>::rebind<void>
+ using void_pointer = memory_internal::ExtractOrT<
+ memory_internal::GetVoidPointer, Alloc,
+ typename absl::pointer_traits<pointer>::template rebind<void>>;
+
+ // const_void_pointer:
+ // Alloc::const_void_pointer if present, otherwise
+ // absl::pointer_traits<pointer>::rebind<const void>
+ using const_void_pointer = memory_internal::ExtractOrT<
+ memory_internal::GetConstVoidPointer, Alloc,
+ typename absl::pointer_traits<pointer>::template rebind<const void>>;
+
+ // difference_type:
+ // Alloc::difference_type if present, otherwise
+ // absl::pointer_traits<pointer>::difference_type
+ using difference_type = memory_internal::ExtractOrT<
+ memory_internal::GetDifferenceType, Alloc,
+ typename absl::pointer_traits<pointer>::difference_type>;
+
+ // size_type:
+ // Alloc::size_type if present, otherwise
+ // std::make_unsigned<difference_type>::type
+ using size_type = memory_internal::ExtractOrT<
+ memory_internal::GetSizeType, Alloc,
+ typename std::make_unsigned<difference_type>::type>;
+
+ // propagate_on_container_copy_assignment:
+ // Alloc::propagate_on_container_copy_assignment if present, otherwise
+ // std::false_type
+ using propagate_on_container_copy_assignment = memory_internal::ExtractOrT<
+ memory_internal::GetPropagateOnContainerCopyAssignment, Alloc,
+ std::false_type>;
+
+ // propagate_on_container_move_assignment:
+ // Alloc::propagate_on_container_move_assignment if present, otherwise
+ // std::false_type
+ using propagate_on_container_move_assignment = memory_internal::ExtractOrT<
+ memory_internal::GetPropagateOnContainerMoveAssignment, Alloc,
+ std::false_type>;
+
+ // propagate_on_container_swap:
+ // Alloc::propagate_on_container_swap if present, otherwise std::false_type
+ using propagate_on_container_swap =
+ memory_internal::ExtractOrT<memory_internal::GetPropagateOnContainerSwap,
+ Alloc, std::false_type>;
+
+ // is_always_equal:
+ // Alloc::is_always_equal if present, otherwise std::is_empty<Alloc>::type
+ using is_always_equal =
+ memory_internal::ExtractOrT<memory_internal::GetIsAlwaysEqual, Alloc,
+ typename std::is_empty<Alloc>::type>;
+
+ // rebind_alloc:
+ // Alloc::rebind<T>::other if present, otherwise Alloc<T, Args> if this Alloc
+ // is Alloc<U, Args>
+ template <typename T>
+ using rebind_alloc = typename memory_internal::RebindAlloc<Alloc, T>::type;
+
+ // rebind_traits:
+ // absl::allocator_traits<rebind_alloc<T>>
+ template <typename T>
+ using rebind_traits = absl::allocator_traits<rebind_alloc<T>>;
+
+ // allocate(Alloc& a, size_type n):
+ // Calls a.allocate(n)
+ static pointer allocate(Alloc& a, // NOLINT(runtime/references)
+ size_type n) {
+ return a.allocate(n);
+ }
+
+ // allocate(Alloc& a, size_type n, const_void_pointer hint):
+ // Calls a.allocate(n, hint) if possible.
+ // If not possible, calls a.allocate(n)
+ static pointer allocate(Alloc& a, size_type n, // NOLINT(runtime/references)
+ const_void_pointer hint) {
+ return allocate_impl(0, a, n, hint);
+ }
+
+ // deallocate(Alloc& a, pointer p, size_type n):
+ // Calls a.deallocate(p, n)
+ static void deallocate(Alloc& a, pointer p, // NOLINT(runtime/references)
+ size_type n) {
+ a.deallocate(p, n);
+ }
+
+ // construct(Alloc& a, T* p, Args&&... args):
+ // Calls a.construct(p, std::forward<Args>(args)...) if possible.
+ // If not possible, calls
+ // ::new (static_cast<void*>(p)) T(std::forward<Args>(args)...)
+ template <typename T, typename... Args>
+ static void construct(Alloc& a, T* p, // NOLINT(runtime/references)
+ Args&&... args) {
+ construct_impl(0, a, p, std::forward<Args>(args)...);
+ }
+
+ // destroy(Alloc& a, T* p):
+ // Calls a.destroy(p) if possible. If not possible, calls p->~T().
+ template <typename T>
+ static void destroy(Alloc& a, T* p) { // NOLINT(runtime/references)
+ destroy_impl(0, a, p);
+ }
+
+ // max_size(const Alloc& a):
+ // Returns a.max_size() if possible. If not possible, returns
+ // std::numeric_limits<size_type>::max() / sizeof(value_type)
+ static size_type max_size(const Alloc& a) { return max_size_impl(0, a); }
+
+ // select_on_container_copy_construction(const Alloc& a):
+ // Returns a.select_on_container_copy_construction() if possible.
+ // If not possible, returns a.
+ static Alloc select_on_container_copy_construction(const Alloc& a) {
+ return select_on_container_copy_construction_impl(0, a);
+ }
+
+ private:
+ template <typename A>
+ static auto allocate_impl(int, A& a, // NOLINT(runtime/references)
+ size_type n, const_void_pointer hint)
+ -> decltype(a.allocate(n, hint)) {
+ return a.allocate(n, hint);
+ }
+ static pointer allocate_impl(char, Alloc& a, // NOLINT(runtime/references)
+ size_type n, const_void_pointer) {
+ return a.allocate(n);
+ }
+
+ template <typename A, typename... Args>
+ static auto construct_impl(int, A& a, // NOLINT(runtime/references)
+ Args&&... args)
+ -> decltype(a.construct(std::forward<Args>(args)...)) {
+ a.construct(std::forward<Args>(args)...);
+ }
+
+ template <typename T, typename... Args>
+ static void construct_impl(char, Alloc&, T* p, Args&&... args) {
+ ::new (static_cast<void*>(p)) T(std::forward<Args>(args)...);
+ }
+
+ template <typename A, typename T>
+ static auto destroy_impl(int, A& a, // NOLINT(runtime/references)
+ T* p) -> decltype(a.destroy(p)) {
+ a.destroy(p);
+ }
+ template <typename T>
+ static void destroy_impl(char, Alloc&, T* p) {
+ p->~T();
+ }
+
+ template <typename A>
+ static auto max_size_impl(int, const A& a) -> decltype(a.max_size()) {
+ return a.max_size();
+ }
+ static size_type max_size_impl(char, const Alloc&) {
+ return (std::numeric_limits<size_type>::max)() / sizeof(value_type);
+ }
+
+ template <typename A>
+ static auto select_on_container_copy_construction_impl(int, const A& a)
+ -> decltype(a.select_on_container_copy_construction()) {
+ return a.select_on_container_copy_construction();
+ }
+ static Alloc select_on_container_copy_construction_impl(char,
+ const Alloc& a) {
+ return a;
+ }
+};
+
+namespace memory_internal {
+
+// This template alias transforms Alloc::is_nothrow into a metafunction with
+// Alloc as a parameter so it can be used with ExtractOrT<>.
+template <typename Alloc>
+using GetIsNothrow = typename Alloc::is_nothrow;
+
+} // namespace memory_internal
+
+// ABSL_ALLOCATOR_NOTHROW is a build time configuration macro for user to
+// specify whether the default allocation function can throw or never throws.
+// If the allocation function never throws, user should define it to a non-zero
+// value (e.g. via `-DABSL_ALLOCATOR_NOTHROW`).
+// If the allocation function can throw, user should leave it undefined or
+// define it to zero.
+//
+// allocator_is_nothrow<Alloc> is a traits class that derives from
+// Alloc::is_nothrow if present, otherwise std::false_type. It's specialized
+// for Alloc = std::allocator<T> for any type T according to the state of
+// ABSL_ALLOCATOR_NOTHROW.
+//
+// default_allocator_is_nothrow is a class that derives from std::true_type
+// when the default allocator (global operator new) never throws, and
+// std::false_type when it can throw. It is a convenience shorthand for writing
+// allocator_is_nothrow<std::allocator<T>> (T can be any type).
+// NOTE: allocator_is_nothrow<std::allocator<T>> is guaranteed to derive from
+// the same type for all T, because users should specialize neither
+// allocator_is_nothrow nor std::allocator.
+template <typename Alloc>
+struct allocator_is_nothrow
+ : memory_internal::ExtractOrT<memory_internal::GetIsNothrow, Alloc,
+ std::false_type> {};
+
+#if ABSL_ALLOCATOR_NOTHROW
+template <typename T>
+struct allocator_is_nothrow<std::allocator<T>> : std::true_type {};
+struct default_allocator_is_nothrow : std::true_type {};
+#else
+struct default_allocator_is_nothrow : std::false_type {};
+#endif
+
+namespace memory_internal {
+template <typename Allocator, typename Iterator, typename... Args>
+void ConstructRange(Allocator& alloc, Iterator first, Iterator last,
+ const Args&... args) {
+ for (Iterator cur = first; cur != last; ++cur) {
+ ABSL_INTERNAL_TRY {
+ std::allocator_traits<Allocator>::construct(alloc, std::addressof(*cur),
+ args...);
+ }
+ ABSL_INTERNAL_CATCH_ANY {
+ while (cur != first) {
+ --cur;
+ std::allocator_traits<Allocator>::destroy(alloc, std::addressof(*cur));
+ }
+ ABSL_INTERNAL_RETHROW;
+ }
+ }
+}
+
+template <typename Allocator, typename Iterator, typename InputIterator>
+void CopyRange(Allocator& alloc, Iterator destination, InputIterator first,
+ InputIterator last) {
+ for (Iterator cur = destination; first != last;
+ static_cast<void>(++cur), static_cast<void>(++first)) {
+ ABSL_INTERNAL_TRY {
+ std::allocator_traits<Allocator>::construct(alloc, std::addressof(*cur),
+ *first);
+ }
+ ABSL_INTERNAL_CATCH_ANY {
+ while (cur != destination) {
+ --cur;
+ std::allocator_traits<Allocator>::destroy(alloc, std::addressof(*cur));
+ }
+ ABSL_INTERNAL_RETHROW;
+ }
+ }
+}
+} // namespace memory_internal
+} // namespace absl
+
+
+#endif // S2_THIRD_PARTY_ABSL_MEMORY_MEMORY_H_
--- /dev/null
+//
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// type_traits.h
+// -----------------------------------------------------------------------------
+//
+// This file contains C++11-compatible versions of standard <type_traits> API
+// functions for determining the characteristics of types. Such traits can
+// support type inference, classification, and transformation, as well as
+// make it easier to write templates based on generic type behavior.
+//
+// See http://en.cppreference.com/w/cpp/header/type_traits
+//
+// WARNING: use of many of the constructs in this header will count as "complex
+// template metaprogramming", so before proceeding, please carefully consider
+// https://google.github.io/styleguide/cppguide.html#Template_metaprogramming
+//
+// WARNING: using template metaprogramming to detect or depend on API
+// features is brittle and not guaranteed. Neither the standard library nor
+// Abseil provides any guarantee that APIs are stable in the face of template
+// metaprogramming. Use with caution.
+#ifndef S2_THIRD_PARTY_ABSL_META_TYPE_TRAITS_H_
+#define S2_THIRD_PARTY_ABSL_META_TYPE_TRAITS_H_
+
+#include <cstddef>
+#include <functional>
+#include <type_traits>
+
+#include "s2/third_party/absl/base/config.h"
+
+namespace absl {
+
+namespace type_traits_internal {
+
+template <typename... Ts>
+struct VoidTImpl {
+ using type = void;
+};
+
+// This trick to retrieve a default alignment is necessary for our
+// implementation of aligned_storage_t to be consistent with any implementation
+// of std::aligned_storage.
+template <size_t Len, typename T = std::aligned_storage<Len>>
+struct default_alignment_of_aligned_storage;
+
+template <size_t Len, size_t Align>
+struct default_alignment_of_aligned_storage<Len,
+ std::aligned_storage<Len, Align>> {
+ static constexpr size_t value = Align;
+};
+
+////////////////////////////////
+// Library Fundamentals V2 TS //
+////////////////////////////////
+
+// NOTE: The `is_detected` family of templates here differ from the library
+// fundamentals specification in that for library fundamentals, `Op<Args...>` is
+// evaluated as soon as the type `is_detected<Op, Args...>` undergoes
+// substitution, regardless of whether or not the `::value` is accessed. That
+// is inconsistent with all other standard traits and prevents lazy evaluation
+// in larger contexts (such as if the `is_detected` check is a trailing argument
+// of a `conjunction`. This implementation opts to instead be lazy in the same
+// way that the standard traits are (this "defect" of the detection idiom
+// specifications has been reported).
+
+template <class Enabler, template <class...> class Op, class... Args>
+struct is_detected_impl {
+ using type = std::false_type;
+};
+
+template <template <class...> class Op, class... Args>
+struct is_detected_impl<typename VoidTImpl<Op<Args...>>::type, Op, Args...> {
+ using type = std::true_type;
+};
+
+template <template <class...> class Op, class... Args>
+struct is_detected : is_detected_impl<void, Op, Args...>::type {};
+
+template <class Enabler, class To, template <class...> class Op, class... Args>
+struct is_detected_convertible_impl {
+ using type = std::false_type;
+};
+
+template <class To, template <class...> class Op, class... Args>
+struct is_detected_convertible_impl<
+ typename std::enable_if<std::is_convertible<Op<Args...>, To>::value>::type,
+ To, Op, Args...> {
+ using type = std::true_type;
+};
+
+template <class To, template <class...> class Op, class... Args>
+struct is_detected_convertible
+ : is_detected_convertible_impl<void, To, Op, Args...>::type {};
+
+template <typename T>
+using IsCopyAssignableImpl =
+ decltype(std::declval<T&>() = std::declval<const T&>());
+
+template <typename T>
+using IsMoveAssignableImpl = decltype(std::declval<T&>() = std::declval<T&&>());
+
+} // namespace type_traits_internal
+
+template <typename T>
+struct is_copy_assignable : type_traits_internal::is_detected<
+ type_traits_internal::IsCopyAssignableImpl, T> {
+};
+
+template <typename T>
+struct is_move_assignable : type_traits_internal::is_detected<
+ type_traits_internal::IsMoveAssignableImpl, T> {
+};
+
+// void_t()
+//
+// Ignores the type of any its arguments and returns `void`. In general, this
+// metafunction allows you to create a general case that maps to `void` while
+// allowing specializations that map to specific types.
+//
+// This metafunction is designed to be a drop-in replacement for the C++17
+// `std::void_t` metafunction.
+//
+// NOTE: `absl::void_t` does not use the standard-specified implementation so
+// that it can remain compatible with gcc < 5.1. This can introduce slightly
+// different behavior, such as when ordering partial specializations.
+template <typename... Ts>
+using void_t = typename type_traits_internal::VoidTImpl<Ts...>::type;
+
+// conjunction
+//
+// Performs a compile-time logical AND operation on the passed types (which
+// must have `::value` members convertible to `bool`. Short-circuits if it
+// encounters any `false` members (and does not compare the `::value` members
+// of any remaining arguments).
+//
+// This metafunction is designed to be a drop-in replacement for the C++17
+// `std::conjunction` metafunction.
+template <typename... Ts>
+struct conjunction;
+
+template <typename T, typename... Ts>
+struct conjunction<T, Ts...>
+ : std::conditional<T::value, conjunction<Ts...>, T>::type {};
+
+template <typename T>
+struct conjunction<T> : T {};
+
+template <>
+struct conjunction<> : std::true_type {};
+
+// disjunction
+//
+// Performs a compile-time logical OR operation on the passed types (which
+// must have `::value` members convertible to `bool`. Short-circuits if it
+// encounters any `true` members (and does not compare the `::value` members
+// of any remaining arguments).
+//
+// This metafunction is designed to be a drop-in replacement for the C++17
+// `std::disjunction` metafunction.
+template <typename... Ts>
+struct disjunction;
+
+template <typename T, typename... Ts>
+struct disjunction<T, Ts...> :
+ std::conditional<T::value, T, disjunction<Ts...>>::type {};
+
+template <typename T>
+struct disjunction<T> : T {};
+
+template <>
+struct disjunction<> : std::false_type {};
+
+// negation
+//
+// Performs a compile-time logical NOT operation on the passed type (which
+// must have `::value` members convertible to `bool`.
+//
+// This metafunction is designed to be a drop-in replacement for the C++17
+// `std::negation` metafunction.
+template <typename T>
+struct negation : std::integral_constant<bool, !T::value> {};
+
+// is_trivially_destructible()
+//
+// Determines whether the passed type `T` is trivially destructable.
+//
+// This metafunction is designed to be a drop-in replacement for the C++11
+// `std::is_trivially_destructible()` metafunction for platforms that have
+// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do
+// fully support C++11, we check whether this yields the same result as the std
+// implementation.
+//
+// NOTE: the extensions (__has_trivial_xxx) are implemented in gcc (version >=
+// 4.3) and clang. Since we are supporting libstdc++ > 4.7, they should always
+// be present. These extensions are documented at
+// https://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html#Type-Traits.
+template <typename T>
+struct is_trivially_destructible
+ : std::integral_constant<bool, __has_trivial_destructor(T) &&
+ std::is_destructible<T>::value> {
+#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
+ private:
+ static constexpr bool compliant = std::is_trivially_destructible<T>::value ==
+ is_trivially_destructible::value;
+ static_assert(compliant || std::is_trivially_destructible<T>::value,
+ "Not compliant with std::is_trivially_destructible; "
+ "Standard: false, Implementation: true");
+ static_assert(compliant || !std::is_trivially_destructible<T>::value,
+ "Not compliant with std::is_trivially_destructible; "
+ "Standard: true, Implementation: false");
+#endif // ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
+};
+
+// is_trivially_default_constructible()
+//
+// Determines whether the passed type `T` is trivially default constructible.
+//
+// This metafunction is designed to be a drop-in replacement for the C++11
+// `std::is_trivially_default_constructible()` metafunction for platforms that
+// have incomplete C++11 support (such as libstdc++ 4.x). On any platforms that
+// do fully support C++11, we check whether this yields the same result as the
+// std implementation.
+//
+// NOTE: according to the C++ standard, Section: 20.15.4.3 [meta.unary.prop]
+// "The predicate condition for a template specialization is_constructible<T,
+// Args...> shall be satisfied if and only if the following variable
+// definition would be well-formed for some invented variable t:
+//
+// T t(declval<Args>()...);
+//
+// is_trivially_constructible<T, Args...> additionally requires that the
+// variable definition does not call any operation that is not trivial.
+// For the purposes of this check, the call to std::declval is considered
+// trivial."
+//
+// Notes from http://en.cppreference.com/w/cpp/types/is_constructible:
+// In many implementations, is_nothrow_constructible also checks if the
+// destructor throws because it is effectively noexcept(T(arg)). Same
+// applies to is_trivially_constructible, which, in these implementations, also
+// requires that the destructor is trivial.
+// GCC bug 51452: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51452
+// LWG issue 2116: http://cplusplus.github.io/LWG/lwg-active.html#2116.
+//
+// "T obj();" need to be well-formed and not call any nontrivial operation.
+// Nontrivially destructible types will cause the expression to be nontrivial.
+template <typename T>
+struct is_trivially_default_constructible
+ : std::integral_constant<bool, __has_trivial_constructor(T) &&
+ std::is_default_constructible<T>::value &&
+ is_trivially_destructible<T>::value> {
+#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
+ private:
+ static constexpr bool compliant =
+ std::is_trivially_default_constructible<T>::value ==
+ is_trivially_default_constructible::value;
+ static_assert(compliant || std::is_trivially_default_constructible<T>::value,
+ "Not compliant with std::is_trivially_default_constructible; "
+ "Standard: false, Implementation: true");
+ static_assert(compliant || !std::is_trivially_default_constructible<T>::value,
+ "Not compliant with std::is_trivially_default_constructible; "
+ "Standard: true, Implementation: false");
+#endif // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
+};
+
+// is_trivially_copy_constructible()
+//
+// Determines whether the passed type `T` is trivially copy constructible.
+//
+// This metafunction is designed to be a drop-in replacement for the C++11
+// `std::is_trivially_copy_constructible()` metafunction for platforms that have
+// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do
+// fully support C++11, we check whether this yields the same result as the std
+// implementation.
+//
+// NOTE: `T obj(declval<const T&>());` needs to be well-formed and not call any
+// nontrivial operation. Nontrivially destructible types will cause the
+// expression to be nontrivial.
+template <typename T>
+struct is_trivially_copy_constructible
+ : std::integral_constant<bool, __has_trivial_copy(T) &&
+ std::is_copy_constructible<T>::value &&
+ is_trivially_destructible<T>::value> {
+#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
+ private:
+ static constexpr bool compliant =
+ std::is_trivially_copy_constructible<T>::value ==
+ is_trivially_copy_constructible::value;
+ static_assert(compliant || std::is_trivially_copy_constructible<T>::value,
+ "Not compliant with std::is_trivially_copy_constructible; "
+ "Standard: false, Implementation: true");
+ static_assert(compliant || !std::is_trivially_copy_constructible<T>::value,
+ "Not compliant with std::is_trivially_copy_constructible; "
+ "Standard: true, Implementation: false");
+#endif // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
+};
+
+// is_trivially_copy_assignable()
+//
+// Determines whether the passed type `T` is trivially copy assignable.
+//
+// This metafunction is designed to be a drop-in replacement for the C++11
+// `std::is_trivially_copy_assignable()` metafunction for platforms that have
+// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do
+// fully support C++11, we check whether this yields the same result as the std
+// implementation.
+//
+// NOTE: `is_assignable<T, U>::value` is `true` if the expression
+// `declval<T>() = declval<U>()` is well-formed when treated as an unevaluated
+// operand. `is_trivially_assignable<T, U>` requires the assignment to call no
+// operation that is not trivial. `is_trivially_copy_assignable<T>` is simply
+// `is_trivially_assignable<T&, const T&>`.
+template <typename T>
+struct is_trivially_copy_assignable
+ : std::integral_constant<
+ bool, __has_trivial_assign(typename std::remove_reference<T>::type) &&
+ absl::is_copy_assignable<T>::value> {
+#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
+ private:
+ static constexpr bool compliant =
+ std::is_trivially_copy_assignable<T>::value ==
+ is_trivially_copy_assignable::value;
+ static_assert(compliant || std::is_trivially_copy_assignable<T>::value,
+ "Not compliant with std::is_trivially_copy_assignable; "
+ "Standard: false, Implementation: true");
+ static_assert(compliant || !std::is_trivially_copy_assignable<T>::value,
+ "Not compliant with std::is_trivially_copy_assignable; "
+ "Standard: true, Implementation: false");
+#endif // ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE
+};
+
+// -----------------------------------------------------------------------------
+// C++14 "_t" trait aliases
+// -----------------------------------------------------------------------------
+
+template <typename T>
+using remove_cv_t = typename std::remove_cv<T>::type;
+
+template <typename T>
+using remove_const_t = typename std::remove_const<T>::type;
+
+template <typename T>
+using remove_volatile_t = typename std::remove_volatile<T>::type;
+
+template <typename T>
+using add_cv_t = typename std::add_cv<T>::type;
+
+template <typename T>
+using add_const_t = typename std::add_const<T>::type;
+
+template <typename T>
+using add_volatile_t = typename std::add_volatile<T>::type;
+
+template <typename T>
+using remove_reference_t = typename std::remove_reference<T>::type;
+
+template <typename T>
+using add_lvalue_reference_t = typename std::add_lvalue_reference<T>::type;
+
+template <typename T>
+using add_rvalue_reference_t = typename std::add_rvalue_reference<T>::type;
+
+template <typename T>
+using remove_pointer_t = typename std::remove_pointer<T>::type;
+
+template <typename T>
+using add_pointer_t = typename std::add_pointer<T>::type;
+
+template <typename T>
+using make_signed_t = typename std::make_signed<T>::type;
+
+template <typename T>
+using make_unsigned_t = typename std::make_unsigned<T>::type;
+
+template <typename T>
+using remove_extent_t = typename std::remove_extent<T>::type;
+
+template <typename T>
+using remove_all_extents_t = typename std::remove_all_extents<T>::type;
+
+template <size_t Len, size_t Align = type_traits_internal::
+ default_alignment_of_aligned_storage<Len>::value>
+using aligned_storage_t = typename std::aligned_storage<Len, Align>::type;
+
+template <typename T>
+using decay_t = typename std::decay<T>::type;
+
+template <bool B, typename T = void>
+using enable_if_t = typename std::enable_if<B, T>::type;
+
+template <bool B, typename T, typename F>
+using conditional_t = typename std::conditional<B, T, F>::type;
+
+template <typename... T>
+using common_type_t = typename std::common_type<T...>::type;
+
+template <typename T>
+using underlying_type_t = typename std::underlying_type<T>::type;
+
+template <typename T>
+using result_of_t = typename std::result_of<T>::type;
+
+namespace type_traits_internal {
+template <typename Key, typename = size_t>
+struct IsHashable : std::false_type {};
+
+template <typename Key>
+struct IsHashable<Key,
+ decltype(std::declval<std::hash<Key>>()(std::declval<Key>()))>
+ : std::true_type {};
+
+template <typename Key>
+struct IsHashEnabled
+ : absl::conjunction<std::is_default_constructible<std::hash<Key>>,
+ std::is_copy_constructible<std::hash<Key>>,
+ std::is_destructible<std::hash<Key>>,
+ absl::is_copy_assignable<std::hash<Key>>,
+ IsHashable<Key>> {};
+
+} // namespace type_traits_internal
+
+} // namespace absl
+
+#endif // S2_THIRD_PARTY_ABSL_META_TYPE_TRAITS_H_
--- /dev/null
+//
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: int128.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines 128-bit integer types.
+//
+//
+//
+
+#ifndef S2_THIRD_PARTY_ABSL_NUMERIC_INT128_H_
+#define S2_THIRD_PARTY_ABSL_NUMERIC_INT128_H_
+
+#include <cassert>
+#include <cmath>
+#include <cstdint>
+#include <cstring>
+#include <iosfwd>
+#include <limits>
+
+#include "s2/third_party/absl/base/config.h"
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/third_party/absl/base/port.h"
+
+namespace absl {
+
+
+// uint128
+//
+// An unsigned 128-bit integer type. The API is meant to mimic an intrinsic type
+// as closely as is practical, including exhibiting undefined behavior in
+// analogous cases (e.g. division by zero). This type is intended to be a
+// drop-in replacement once C++ supports an intrinsic `uint128_t` type; when
+// that occurs, existing well-behaved uses of `uint128` will continue to work
+// using that new type.
+//
+// Note: code written with this type will continue to compile once `uint128_t`
+// is introduced, provided the replacement helper functions
+// `Uint128(Low|High)64()` and `MakeUint128()` are made.
+//
+// A `uint128` supports the following:
+//
+// * Implicit construction from integral types
+// * Explicit conversion to integral types
+//
+// Additionally, if your compiler supports `__int128`, `uint128` is
+// interoperable with that type. (Abseil checks for this compatibility through
+// the `ABSL_HAVE_INTRINSIC_INT128` macro.)
+//
+// However, a `uint128` differs from intrinsic integral types in the following
+// ways:
+//
+// * Errors on implicit conversions that do not preserve value (such as
+// loss of precision when converting to float values).
+// * Requires explicit construction from and conversion to floating point
+// types.
+// * Conversion to integral types requires an explicit static_cast() to
+// mimic use of the `-Wnarrowing` compiler flag.
+// * The alignment requirement of `uint128` may differ from that of an
+// intrinsic 128-bit integer type depending on platform and build
+// configuration.
+//
+// Example:
+//
+// float y = absl::Uint128Max(); // Error. uint128 cannot be implicitly
+// // converted to float.
+//
+// absl::uint128 v;
+// uint64_t i = v; // Error
+// uint64_t i = static_cast<uint64_t>(v); // OK
+//
+class alignas(16) uint128 {
+ public:
+ uint128() = default;
+
+ // Constructors from arithmetic types
+ constexpr uint128(int v); // NOLINT(runtime/explicit)
+ constexpr uint128(unsigned int v); // NOLINT(runtime/explicit)
+ constexpr uint128(long v); // NOLINT(runtime/int)
+ constexpr uint128(unsigned long v); // NOLINT(runtime/int)
+ constexpr uint128(long long v); // NOLINT(runtime/int)
+ constexpr uint128(unsigned long long v); // NOLINT(runtime/int)
+#ifdef ABSL_HAVE_INTRINSIC_INT128
+__extension__ constexpr uint128(__int128 v); // NOLINT(runtime/explicit)
+__extension__ constexpr uint128(unsigned __int128 v); // NOLINT(runtime/explicit)
+#endif // ABSL_HAVE_INTRINSIC_INT128
+ explicit uint128(float v);
+ explicit uint128(double v);
+ explicit uint128(long double v);
+
+ // Assignment operators from arithmetic types
+ uint128& operator=(int v);
+ uint128& operator=(unsigned int v);
+ uint128& operator=(long v); // NOLINT(runtime/int)
+ uint128& operator=(unsigned long v); // NOLINT(runtime/int)
+ uint128& operator=(long long v); // NOLINT(runtime/int)
+ uint128& operator=(unsigned long long v); // NOLINT(runtime/int)
+#ifdef ABSL_HAVE_INTRINSIC_INT128
+__extension__ uint128& operator=(__int128 v);
+__extension__ uint128& operator=(unsigned __int128 v);
+#endif // ABSL_HAVE_INTRINSIC_INT128
+
+ // Conversion operators to other arithmetic types
+ constexpr explicit operator bool() const;
+ constexpr explicit operator char() const;
+ constexpr explicit operator signed char() const;
+ constexpr explicit operator unsigned char() const;
+ constexpr explicit operator char16_t() const;
+ constexpr explicit operator char32_t() const;
+ constexpr explicit operator wchar_t() const;
+ constexpr explicit operator short() const; // NOLINT(runtime/int)
+ // NOLINTNEXTLINE(runtime/int)
+ constexpr explicit operator unsigned short() const;
+ constexpr explicit operator int() const;
+ constexpr explicit operator unsigned int() const;
+ constexpr explicit operator long() const; // NOLINT(runtime/int)
+ // NOLINTNEXTLINE(runtime/int)
+ constexpr explicit operator unsigned long() const;
+ // NOLINTNEXTLINE(runtime/int)
+ constexpr explicit operator long long() const;
+ // NOLINTNEXTLINE(runtime/int)
+ constexpr explicit operator unsigned long long() const;
+#ifdef ABSL_HAVE_INTRINSIC_INT128
+__extension__ constexpr explicit operator __int128() const;
+__extension__ constexpr explicit operator unsigned __int128() const;
+#endif // ABSL_HAVE_INTRINSIC_INT128
+ explicit operator float() const;
+ explicit operator double() const;
+ explicit operator long double() const;
+
+ // Trivial copy constructor, assignment operator and destructor.
+
+ // Arithmetic operators.
+ uint128& operator+=(uint128 other);
+ uint128& operator-=(uint128 other);
+ uint128& operator*=(uint128 other);
+ // Long division/modulo for uint128.
+ uint128& operator/=(uint128 other);
+ uint128& operator%=(uint128 other);
+ uint128 operator++(int);
+ uint128 operator--(int);
+ uint128& operator<<=(int);
+ uint128& operator>>=(int);
+ uint128& operator&=(uint128 other);
+ uint128& operator|=(uint128 other);
+ uint128& operator^=(uint128 other);
+ uint128& operator++();
+ uint128& operator--();
+
+ // Uint128Low64()
+ //
+ // Returns the lower 64-bit value of a `uint128` value.
+ friend constexpr uint64_t Uint128Low64(uint128 v);
+
+ // Uint128High64()
+ //
+ // Returns the higher 64-bit value of a `uint128` value.
+ friend constexpr uint64_t Uint128High64(uint128 v);
+
+ // MakeUInt128()
+ //
+ // Constructs a `uint128` numeric value from two 64-bit unsigned integers.
+ // Note that this factory function is the only way to construct a `uint128`
+ // from integer values greater than 2^64.
+ //
+ // Example:
+ //
+ // absl::uint128 big = absl::MakeUint128(1, 0);
+ friend constexpr uint128 MakeUint128(uint64_t high, uint64_t low);
+
+ // Uint128Max()
+ //
+ // Returns the highest value for a 128-bit unsigned integer.
+ friend constexpr uint128 Uint128Max();
+
+ private:
+ constexpr uint128(uint64_t high, uint64_t low);
+
+ // TODO(user) Update implementation to use __int128 once all users of
+ // uint128 are fixed to not depend on alignof(uint128) == 8. Also add
+ // alignas(16) to class definition to keep alignment consistent across
+ // platforms.
+#if defined(ABSL_IS_LITTLE_ENDIAN)
+ uint64_t lo_;
+ uint64_t hi_;
+#elif defined(ABSL_IS_BIG_ENDIAN)
+ uint64_t hi_;
+ uint64_t lo_;
+#else // byte order
+#error "Unsupported byte order: must be little-endian or big-endian."
+#endif // byte order
+};
+
+
+// allow uint128 to be logged
+std::ostream& operator<<(std::ostream& os, uint128 v);
+
+// TODO(user) add operator>>(std::istream&, uint128)
+
+
+// --------------------------------------------------------------------------
+// Implementation details follow
+// --------------------------------------------------------------------------
+
+constexpr uint128 MakeUint128(uint64_t high, uint64_t low) {
+ return uint128(high, low);
+}
+
+constexpr uint128 Uint128Max() {
+ return uint128(std::numeric_limits<uint64_t>::max(),
+ std::numeric_limits<uint64_t>::max());
+}
+
+// Assignment from integer types.
+
+inline uint128& uint128::operator=(int v) { return *this = uint128(v); }
+
+inline uint128& uint128::operator=(unsigned int v) {
+ return *this = uint128(v);
+}
+
+inline uint128& uint128::operator=(long v) { // NOLINT(runtime/int)
+ return *this = uint128(v);
+}
+
+// NOLINTNEXTLINE(runtime/int)
+inline uint128& uint128::operator=(unsigned long v) {
+ return *this = uint128(v);
+}
+
+// NOLINTNEXTLINE(runtime/int)
+inline uint128& uint128::operator=(long long v) {
+ return *this = uint128(v);
+}
+
+// NOLINTNEXTLINE(runtime/int)
+inline uint128& uint128::operator=(unsigned long long v) {
+ return *this = uint128(v);
+}
+
+#ifdef ABSL_HAVE_INTRINSIC_INT128
+__extension__ inline uint128& uint128::operator=(__int128 v) {
+ return *this = uint128(v);
+}
+
+__extension__ inline uint128& uint128::operator=(unsigned __int128 v) {
+ return *this = uint128(v);
+}
+#endif // ABSL_HAVE_INTRINSIC_INT128
+
+
+// Arithmetic operators.
+
+uint128 operator<<(uint128 lhs, int amount);
+uint128 operator>>(uint128 lhs, int amount);
+uint128 operator+(uint128 lhs, uint128 rhs);
+uint128 operator-(uint128 lhs, uint128 rhs);
+uint128 operator*(uint128 lhs, uint128 rhs);
+uint128 operator/(uint128 lhs, uint128 rhs);
+uint128 operator%(uint128 lhs, uint128 rhs);
+
+inline uint128& uint128::operator<<=(int amount) {
+ *this = *this << amount;
+ return *this;
+}
+
+inline uint128& uint128::operator>>=(int amount) {
+ *this = *this >> amount;
+ return *this;
+}
+
+inline uint128& uint128::operator+=(uint128 other) {
+ *this = *this + other;
+ return *this;
+}
+
+inline uint128& uint128::operator-=(uint128 other) {
+ *this = *this - other;
+ return *this;
+}
+
+inline uint128& uint128::operator*=(uint128 other) {
+ *this = *this * other;
+ return *this;
+}
+
+inline uint128& uint128::operator/=(uint128 other) {
+ *this = *this / other;
+ return *this;
+}
+
+inline uint128& uint128::operator%=(uint128 other) {
+ *this = *this % other;
+ return *this;
+}
+
+constexpr uint64_t Uint128Low64(uint128 v) { return v.lo_; }
+
+constexpr uint64_t Uint128High64(uint128 v) { return v.hi_; }
+
+// Constructors from integer types.
+
+#if defined(ABSL_IS_LITTLE_ENDIAN)
+
+constexpr uint128::uint128(uint64_t high, uint64_t low)
+ : lo_{low}, hi_{high} {}
+
+constexpr uint128::uint128(int v)
+ : lo_{static_cast<uint64_t>(v)},
+ hi_{v < 0 ? std::numeric_limits<uint64_t>::max() : 0} {}
+constexpr uint128::uint128(long v) // NOLINT(runtime/int)
+ : lo_{static_cast<uint64_t>(v)},
+ hi_{v < 0 ? std::numeric_limits<uint64_t>::max() : 0} {}
+constexpr uint128::uint128(long long v) // NOLINT(runtime/int)
+ : lo_{static_cast<uint64_t>(v)},
+ hi_{v < 0 ? std::numeric_limits<uint64_t>::max() : 0} {}
+
+constexpr uint128::uint128(unsigned int v) : lo_{v}, hi_{0} {}
+// NOLINTNEXTLINE(runtime/int)
+constexpr uint128::uint128(unsigned long v) : lo_{v}, hi_{0} {}
+// NOLINTNEXTLINE(runtime/int)
+constexpr uint128::uint128(unsigned long long v) : lo_{v}, hi_{0} {}
+
+#ifdef ABSL_HAVE_INTRINSIC_INT128
+__extension__ constexpr uint128::uint128(__int128 v)
+ : lo_{static_cast<uint64_t>(v & ~uint64_t{0})},
+ hi_{static_cast<uint64_t>(static_cast<unsigned __int128>(v) >> 64)} {}
+__extension__ constexpr uint128::uint128(unsigned __int128 v)
+ : lo_{static_cast<uint64_t>(v & ~uint64_t{0})},
+ hi_{static_cast<uint64_t>(v >> 64)} {}
+#endif // ABSL_HAVE_INTRINSIC_INT128
+
+
+#elif defined(ABSL_IS_BIG_ENDIAN)
+
+constexpr uint128::uint128(uint64_t high, uint64_t low)
+ : hi_{high}, lo_{low} {}
+
+constexpr uint128::uint128(int v)
+ : hi_{v < 0 ? std::numeric_limits<uint64_t>::max() : 0},
+ lo_{static_cast<uint64_t>(v)} {}
+constexpr uint128::uint128(long v) // NOLINT(runtime/int)
+ : hi_{v < 0 ? std::numeric_limits<uint64_t>::max() : 0},
+ lo_{static_cast<uint64_t>(v)} {}
+constexpr uint128::uint128(long long v) // NOLINT(runtime/int)
+ : hi_{v < 0 ? std::numeric_limits<uint64_t>::max() : 0},
+ lo_{static_cast<uint64_t>(v)} {}
+
+constexpr uint128::uint128(unsigned int v) : hi_{0}, lo_{v} {}
+// NOLINTNEXTLINE(runtime/int)
+constexpr uint128::uint128(unsigned long v) : hi_{0}, lo_{v} {}
+// NOLINTNEXTLINE(runtime/int)
+constexpr uint128::uint128(unsigned long long v) : hi_{0}, lo_{v} {}
+
+#ifdef ABSL_HAVE_INTRINSIC_INT128
+__extension__ constexpr uint128::uint128(__int128 v)
+ : hi_{static_cast<uint64_t>(static_cast<unsigned __int128>(v) >> 64)},
+ lo_{static_cast<uint64_t>(v & ~uint64_t{0})} {}
+__extension__ constexpr uint128::uint128(unsigned __int128 v)
+ : hi_{static_cast<uint64_t>(v >> 64)},
+ lo_{static_cast<uint64_t>(v & ~uint64_t{0})} {}
+#endif // ABSL_HAVE_INTRINSIC_INT128
+
+
+#else // byte order
+#error "Unsupported byte order: must be little-endian or big-endian."
+#endif // byte order
+
+// Conversion operators to integer types.
+
+constexpr uint128::operator bool() const { return lo_ || hi_; }
+
+constexpr uint128::operator char() const { return static_cast<char>(lo_); }
+
+constexpr uint128::operator signed char() const {
+ return static_cast<signed char>(lo_);
+}
+
+constexpr uint128::operator unsigned char() const {
+ return static_cast<unsigned char>(lo_);
+}
+
+constexpr uint128::operator char16_t() const {
+ return static_cast<char16_t>(lo_);
+}
+
+constexpr uint128::operator char32_t() const {
+ return static_cast<char32_t>(lo_);
+}
+
+constexpr uint128::operator wchar_t() const {
+ return static_cast<wchar_t>(lo_);
+}
+
+// NOLINTNEXTLINE(runtime/int)
+constexpr uint128::operator short() const { return static_cast<short>(lo_); }
+
+constexpr uint128::operator unsigned short() const { // NOLINT(runtime/int)
+ return static_cast<unsigned short>(lo_); // NOLINT(runtime/int)
+}
+
+constexpr uint128::operator int() const { return static_cast<int>(lo_); }
+
+constexpr uint128::operator unsigned int() const {
+ return static_cast<unsigned int>(lo_);
+}
+
+// NOLINTNEXTLINE(runtime/int)
+constexpr uint128::operator long() const { return static_cast<long>(lo_); }
+
+constexpr uint128::operator unsigned long() const { // NOLINT(runtime/int)
+ return static_cast<unsigned long>(lo_); // NOLINT(runtime/int)
+}
+
+constexpr uint128::operator long long() const { // NOLINT(runtime/int)
+ return static_cast<long long>(lo_); // NOLINT(runtime/int)
+}
+
+constexpr uint128::operator unsigned long long() const { // NOLINT(runtime/int)
+ return static_cast<unsigned long long>(lo_); // NOLINT(runtime/int)
+}
+
+#ifdef ABSL_HAVE_INTRINSIC_INT128
+__extension__ constexpr uint128::operator __int128() const {
+ return (static_cast<__int128>(hi_) << 64) + lo_;
+}
+
+__extension__ constexpr uint128::operator unsigned __int128() const {
+ return (static_cast<unsigned __int128>(hi_) << 64) + lo_;
+}
+#endif // ABSL_HAVE_INTRINSIC_INT128
+
+// Conversion operators to floating point types.
+
+inline uint128::operator float() const {
+ return static_cast<float>(lo_) + std::ldexp(static_cast<float>(hi_), 64);
+}
+
+inline uint128::operator double() const {
+ return static_cast<double>(lo_) + std::ldexp(static_cast<double>(hi_), 64);
+}
+
+inline uint128::operator long double() const {
+ return static_cast<long double>(lo_) +
+ std::ldexp(static_cast<long double>(hi_), 64);
+}
+
+// Comparison operators.
+
+inline bool operator==(uint128 lhs, uint128 rhs) {
+ return (Uint128Low64(lhs) == Uint128Low64(rhs) &&
+ Uint128High64(lhs) == Uint128High64(rhs));
+}
+
+inline bool operator!=(uint128 lhs, uint128 rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator<(uint128 lhs, uint128 rhs) {
+ return (Uint128High64(lhs) == Uint128High64(rhs))
+ ? (Uint128Low64(lhs) < Uint128Low64(rhs))
+ : (Uint128High64(lhs) < Uint128High64(rhs));
+}
+
+inline bool operator>(uint128 lhs, uint128 rhs) {
+ return (Uint128High64(lhs) == Uint128High64(rhs))
+ ? (Uint128Low64(lhs) > Uint128Low64(rhs))
+ : (Uint128High64(lhs) > Uint128High64(rhs));
+}
+
+inline bool operator<=(uint128 lhs, uint128 rhs) {
+ return (Uint128High64(lhs) == Uint128High64(rhs))
+ ? (Uint128Low64(lhs) <= Uint128Low64(rhs))
+ : (Uint128High64(lhs) <= Uint128High64(rhs));
+}
+
+inline bool operator>=(uint128 lhs, uint128 rhs) {
+ return (Uint128High64(lhs) == Uint128High64(rhs))
+ ? (Uint128Low64(lhs) >= Uint128Low64(rhs))
+ : (Uint128High64(lhs) >= Uint128High64(rhs));
+}
+
+// Unary operators.
+
+inline uint128 operator-(uint128 val) {
+ uint64_t hi = ~Uint128High64(val);
+ uint64_t lo = ~Uint128Low64(val) + 1;
+ if (lo == 0) ++hi; // carry
+ return MakeUint128(hi, lo);
+}
+
+inline bool operator!(uint128 val) {
+ return !Uint128High64(val) && !Uint128Low64(val);
+}
+
+// Logical operators.
+
+inline uint128 operator~(uint128 val) {
+ return MakeUint128(~Uint128High64(val), ~Uint128Low64(val));
+}
+
+inline uint128 operator|(uint128 lhs, uint128 rhs) {
+ return MakeUint128(Uint128High64(lhs) | Uint128High64(rhs),
+ Uint128Low64(lhs) | Uint128Low64(rhs));
+}
+
+inline uint128 operator&(uint128 lhs, uint128 rhs) {
+ return MakeUint128(Uint128High64(lhs) & Uint128High64(rhs),
+ Uint128Low64(lhs) & Uint128Low64(rhs));
+}
+
+inline uint128 operator^(uint128 lhs, uint128 rhs) {
+ return MakeUint128(Uint128High64(lhs) ^ Uint128High64(rhs),
+ Uint128Low64(lhs) ^ Uint128Low64(rhs));
+}
+
+inline uint128& uint128::operator|=(uint128 other) {
+ hi_ |= other.hi_;
+ lo_ |= other.lo_;
+ return *this;
+}
+
+inline uint128& uint128::operator&=(uint128 other) {
+ hi_ &= other.hi_;
+ lo_ &= other.lo_;
+ return *this;
+}
+
+inline uint128& uint128::operator^=(uint128 other) {
+ hi_ ^= other.hi_;
+ lo_ ^= other.lo_;
+ return *this;
+}
+
+// Arithmetic operators.
+
+inline uint128 operator<<(uint128 lhs, int amount) {
+ // uint64_t shifts of >= 64 are undefined, so we will need some
+ // special-casing.
+ if (amount < 64) {
+ if (amount != 0) {
+ return MakeUint128(
+ (Uint128High64(lhs) << amount) | (Uint128Low64(lhs) >> (64 - amount)),
+ Uint128Low64(lhs) << amount);
+ }
+ return lhs;
+ }
+ return MakeUint128(Uint128Low64(lhs) << (amount - 64), 0);
+}
+
+inline uint128 operator>>(uint128 lhs, int amount) {
+ // uint64_t shifts of >= 64 are undefined, so we will need some
+ // special-casing.
+ if (amount < 64) {
+ if (amount != 0) {
+ return MakeUint128(Uint128High64(lhs) >> amount,
+ (Uint128Low64(lhs) >> amount) |
+ (Uint128High64(lhs) << (64 - amount)));
+ }
+ return lhs;
+ }
+ return MakeUint128(0, Uint128High64(lhs) >> (amount - 64));
+}
+
+inline uint128 operator+(uint128 lhs, uint128 rhs) {
+ uint128 result = MakeUint128(Uint128High64(lhs) + Uint128High64(rhs),
+ Uint128Low64(lhs) + Uint128Low64(rhs));
+ if (Uint128Low64(result) < Uint128Low64(lhs)) { // check for carry
+ return MakeUint128(Uint128High64(result) + 1, Uint128Low64(result));
+ }
+ return result;
+}
+
+inline uint128 operator-(uint128 lhs, uint128 rhs) {
+ uint128 result = MakeUint128(Uint128High64(lhs) - Uint128High64(rhs),
+ Uint128Low64(lhs) - Uint128Low64(rhs));
+ if (Uint128Low64(lhs) < Uint128Low64(rhs)) { // check for carry
+ return MakeUint128(Uint128High64(result) - 1, Uint128Low64(result));
+ }
+ return result;
+}
+
+inline uint128 operator*(uint128 lhs, uint128 rhs) {
+#if defined(ABSL_HAVE_INTRINSIC_INT128)
+ // TODO(user) Remove once alignment issues are resolved and unsigned __int128
+ // can be used for uint128 storage.
+ return __extension__ static_cast<unsigned __int128>(lhs) *
+ __extension__ static_cast<unsigned __int128>(rhs);
+#else // ABSL_HAVE_INTRINSIC128
+ uint64_t a32 = Uint128Low64(lhs) >> 32;
+ uint64_t a00 = Uint128Low64(lhs) & 0xffffffff;
+ uint64_t b32 = Uint128Low64(rhs) >> 32;
+ uint64_t b00 = Uint128Low64(rhs) & 0xffffffff;
+ uint128 result =
+ MakeUint128(Uint128High64(lhs) * Uint128Low64(rhs) +
+ Uint128Low64(lhs) * Uint128High64(rhs) + a32 * b32,
+ a00 * b00);
+ result += uint128(a32 * b00) << 32;
+ result += uint128(a00 * b32) << 32;
+ return result;
+#endif // ABSL_HAVE_INTRINSIC128
+}
+
+// Increment/decrement operators.
+
+inline uint128 uint128::operator++(int) {
+ uint128 tmp(*this);
+ *this += 1;
+ return tmp;
+}
+
+inline uint128 uint128::operator--(int) {
+ uint128 tmp(*this);
+ *this -= 1;
+ return tmp;
+}
+
+inline uint128& uint128::operator++() {
+ *this += 1;
+ return *this;
+}
+
+inline uint128& uint128::operator--() {
+ *this -= 1;
+ return *this;
+}
+
+
+
+
+#if defined(ABSL_HAVE_INTRINSIC_INT128)
+#include "s2/third_party/absl/numeric/int128_have_intrinsic.inc"
+#else // ABSL_HAVE_INTRINSIC_INT128
+#include "s2/third_party/absl/numeric/int128_no_intrinsic.inc"
+#endif // ABSL_HAVE_INTRINSIC_INT128
+
+} // namespace absl
+
+
+
+
+#endif // S2_THIRD_PARTY_ABSL_NUMERIC_INT128_H_
--- /dev/null
+// This file will contain :int128 implementation details that depend on internal
+// representation when ABSL_HAVE_INTRINSIC_INT128 is defined. This file will be
+// included by int128.h.
--- /dev/null
+// This file will contain :int128 implementation details that depend on internal
+// representation when ABSL_HAVE_INTRINSIC_INT128 is *not* defined. This file
+// will be included by int128.h.
--- /dev/null
+//
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: ascii.h
+// -----------------------------------------------------------------------------
+//
+// This package contains functions operating on characters and strings
+// restricted to standard ASCII. These include character classification
+// functions analogous to those found in the ANSI C Standard Library <ctype.h>
+// header file.
+//
+// C++ implementations provide <ctype.h> functionality based on their
+// C environment locale. In general, reliance on such a locale is not ideal, as
+// the locale standard is problematic (and may not return invariant information
+// for the same character set, for example). These `ascii_*()` functions are
+// hard-wired for standard ASCII, much faster, and guaranteed to behave
+// consistently. They will never be overloaded, nor will their function
+// signature change.
+//
+// `ascii_isalnum()`, `ascii_isalpha()`, `ascii_isascii()`, `ascii_isblank()`,
+// `ascii_iscntrl()`, `ascii_isdigit()`, `ascii_isgraph()`, `ascii_islower()`,
+// `ascii_isprint()`, `ascii_ispunct()`, `ascii_isspace()`, `ascii_isupper()`,
+// `ascii_isxdigit()`
+// Analogous to the <ctype.h> functions with similar names, these
+// functions take an unsigned char and return a bool, based on whether the
+// character matches the condition specified.
+//
+// If the input character has a numerical value greater than 127, these
+// functions return `false`.
+//
+// `ascii_tolower()`, `ascii_toupper()`
+// Analogous to the <ctype.h> functions with similar names, these functions
+// take an unsigned char and return a char.
+//
+// If the input character is not an ASCII {lower,upper}-case letter (including
+// numerical values greater than 127) then the functions return the same value
+// as the input character.
+
+#ifndef S2_THIRD_PARTY_ABSL_STRINGS_ASCII_H_
+#define S2_THIRD_PARTY_ABSL_STRINGS_ASCII_H_
+
+#include <algorithm>
+#include <string>
+
+#include "s2/third_party/absl/base/attributes.h"
+#include "s2/third_party/absl/strings/string_view.h"
+
+namespace absl {
+namespace ascii_internal {
+
+// Declaration for an array of bitfields holding character information.
+extern const unsigned char kPropertyBits[256];
+
+// Declaration for the array of characters to upper-case characters.
+extern const char kToUpper[256];
+
+// Declaration for the array of characters to lower-case characters.
+extern const char kToLower[256];
+
+} // namespace ascii_internal
+
+// ascii_isalpha()
+//
+// Determines whether the given character is an alphabetic character.
+inline bool ascii_isalpha(unsigned char c) {
+ return (ascii_internal::kPropertyBits[c] & 0x01) != 0;
+}
+
+// ascii_isalnum()
+//
+// Determines whether the given character is an alphanumeric character.
+inline bool ascii_isalnum(unsigned char c) {
+ return (ascii_internal::kPropertyBits[c] & 0x04) != 0;
+}
+
+// ascii_isspace()
+//
+// Determines whether the given character is a whitespace character (space,
+// tab, vertical tab, formfeed, linefeed, or carriage return).
+inline bool ascii_isspace(unsigned char c) {
+ return (ascii_internal::kPropertyBits[c] & 0x08) != 0;
+}
+
+// ascii_ispunct()
+//
+// Determines whether the given character is a punctuation character.
+inline bool ascii_ispunct(unsigned char c) {
+ return (ascii_internal::kPropertyBits[c] & 0x10) != 0;
+}
+
+// ascii_isblank()
+//
+// Determines whether the given character is a blank character (tab or space).
+inline bool ascii_isblank(unsigned char c) {
+ return (ascii_internal::kPropertyBits[c] & 0x20) != 0;
+}
+
+// ascii_iscntrl()
+//
+// Determines whether the given character is a control character.
+inline bool ascii_iscntrl(unsigned char c) {
+ return (ascii_internal::kPropertyBits[c] & 0x40) != 0;
+}
+
+// ascii_isxdigit()
+//
+// Determines whether the given character can be represented as a hexadecimal
+// digit character (i.e. {0-9} or {A-F}).
+inline bool ascii_isxdigit(unsigned char c) {
+ return (ascii_internal::kPropertyBits[c] & 0x80) != 0;
+}
+
+// ascii_isdigit()
+//
+// Determines whether the given character can be represented as a decimal
+// digit character (i.e. {0-9}).
+inline bool ascii_isdigit(unsigned char c) { return c >= '0' && c <= '9'; }
+
+// ascii_isprint()
+//
+// Determines whether the given character is printable, including whitespace.
+inline bool ascii_isprint(unsigned char c) { return c >= 32 && c < 127; }
+
+// ascii_isgraph()
+//
+// Determines whether the given character has a graphical representation.
+inline bool ascii_isgraph(unsigned char c) { return c > 32 && c < 127; }
+
+// ascii_isupper()
+//
+// Determines whether the given character is uppercase.
+inline bool ascii_isupper(unsigned char c) { return c >= 'A' && c <= 'Z'; }
+
+// ascii_islower()
+//
+// Determines whether the given character is lowercase.
+inline bool ascii_islower(unsigned char c) { return c >= 'a' && c <= 'z'; }
+
+// ascii_isascii()
+//
+// Determines whether the given character is ASCII.
+inline bool ascii_isascii(unsigned char c) { return c < 128; }
+
+// ascii_tolower()
+//
+// Returns an ASCII character, converting to lowercase if uppercase is
+// passed. Note that character values > 127 are simply returned.
+inline char ascii_tolower(unsigned char c) {
+ return ascii_internal::kToLower[c];
+}
+
+// Converts the characters in `s` to lowercase, changing the contents of `s`.
+void AsciiStrToLower(string* s);
+
+// Creates a lowercase string from a given absl::string_view.
+ABSL_MUST_USE_RESULT inline string AsciiStrToLower(absl::string_view s) {
+ string result(s);
+ absl::AsciiStrToLower(&result);
+ return result;
+}
+
+// ascii_toupper()
+//
+// Returns the ASCII character, converting to upper-case if lower-case is
+// passed. Note that characters values > 127 are simply returned.
+inline char ascii_toupper(unsigned char c) {
+ return ascii_internal::kToUpper[c];
+}
+
+// Converts the characters in `s` to uppercase, changing the contents of `s`.
+void AsciiStrToUpper(string* s);
+
+// Creates an uppercase string from a given absl::string_view.
+ABSL_MUST_USE_RESULT inline string AsciiStrToUpper(absl::string_view s) {
+ string result(s);
+ absl::AsciiStrToUpper(&result);
+ return result;
+}
+
+// Returns absl::string_view with whitespace stripped from the beginning of the
+// given string_view.
+ABSL_MUST_USE_RESULT inline absl::string_view StripLeadingAsciiWhitespace(
+ absl::string_view str) {
+ auto it = std::find_if_not(str.begin(), str.end(), absl::ascii_isspace);
+ return str.substr(it - str.begin());
+}
+
+// Strips in place whitespace from the beginning of the given string.
+inline void StripLeadingAsciiWhitespace(string* str) {
+ auto it = std::find_if_not(str->begin(), str->end(), absl::ascii_isspace);
+ str->erase(str->begin(), it);
+}
+
+// Returns absl::string_view with whitespace stripped from the end of the given
+// string_view.
+ABSL_MUST_USE_RESULT inline absl::string_view StripTrailingAsciiWhitespace(
+ absl::string_view str) {
+ auto it = std::find_if_not(str.rbegin(), str.rend(), absl::ascii_isspace);
+ return str.substr(0, str.rend() - it);
+}
+
+// Strips in place whitespace from the end of the given string
+inline void StripTrailingAsciiWhitespace(string* str) {
+ auto it = std::find_if_not(str->rbegin(), str->rend(), absl::ascii_isspace);
+ str->erase(str->rend() - it);
+}
+
+// Returns absl::string_view with whitespace stripped from both ends of the
+// given string_view.
+ABSL_MUST_USE_RESULT inline absl::string_view StripAsciiWhitespace(
+ absl::string_view str) {
+ return StripTrailingAsciiWhitespace(StripLeadingAsciiWhitespace(str));
+}
+
+// Strips in place whitespace from both ends of the given string
+inline void StripAsciiWhitespace(string* str) {
+ StripTrailingAsciiWhitespace(str);
+ StripLeadingAsciiWhitespace(str);
+}
+
+// Removes leading, trailing, and consecutive internal whitespace.
+void RemoveExtraAsciiWhitespace(string*);
+
+} // namespace absl
+
+#endif // S2_THIRD_PARTY_ABSL_STRINGS_ASCII_H_
--- /dev/null
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef S2_THIRD_PARTY_ABSL_STRINGS_ASCII_CTYPE_H_
+#define S2_THIRD_PARTY_ABSL_STRINGS_ASCII_CTYPE_H_
+
+#include "s2/third_party/absl/strings/ascii.h"
+
+inline bool ascii_isalpha(unsigned char c) {
+ return absl::ascii_isalpha(c);
+}
+inline bool ascii_isalnum(unsigned char c) {
+ return absl::ascii_isalnum(c);
+}
+inline bool ascii_isspace(unsigned char c) {
+ return absl::ascii_isspace(c);
+}
+inline bool ascii_ispunct(unsigned char c) {
+ return absl::ascii_ispunct(c);
+}
+inline bool ascii_isblank(unsigned char c) {
+ return absl::ascii_isblank(c);
+}
+inline bool ascii_iscntrl(unsigned char c) {
+ return absl::ascii_iscntrl(c);
+}
+inline bool ascii_isxdigit(unsigned char c) {
+ return absl::ascii_isxdigit(c);
+}
+inline bool ascii_isdigit(unsigned char c) {
+ return absl::ascii_isdigit(c);
+}
+inline bool ascii_isprint(unsigned char c) {
+ return absl::ascii_isprint(c);
+}
+inline bool ascii_isgraph(unsigned char c) {
+ return absl::ascii_isgraph(c);
+}
+inline bool ascii_isupper(unsigned char c) {
+ return absl::ascii_isupper(c);
+}
+inline bool ascii_islower(unsigned char c) {
+ return absl::ascii_islower(c);
+}
+inline bool ascii_isascii(unsigned char c) {
+ return absl::ascii_isascii(c);
+}
+inline char ascii_tolower(unsigned char c) {
+ return absl::ascii_tolower(c);
+}
+inline char ascii_toupper(unsigned char c) {
+ return absl::ascii_toupper(c);
+}
+
+#endif // S2_THIRD_PARTY_ABSL_STRINGS_ASCII_CTYPE_H_
--- /dev/null
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef S2_THIRD_PARTY_ABSL_STRINGS_INTERNAL_BITS_H_
+#define S2_THIRD_PARTY_ABSL_STRINGS_INTERNAL_BITS_H_
+
+#include <cstdint>
+
+#if defined(_MSC_VER) && defined(_M_X64)
+#include <intrin.h>
+#pragma intrinsic(_BitScanReverse64)
+#endif
+
+namespace absl {
+namespace strings_internal {
+
+// Returns the number of leading 0 bits in a 64-bit value.
+inline int CountLeadingZeros64(uint64_t n) {
+#if defined(__GNUC__)
+ static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int)
+ "__builtin_clzll does not take 64bit arg");
+ return n == 0 ? 64 : __builtin_clzll(n);
+#elif defined(_MSC_VER) && defined(_M_X64)
+ unsigned long result; // NOLINT(runtime/int)
+ if (_BitScanReverse64(&result, n)) {
+ return 63 - result;
+ }
+ return 64;
+#else
+ int zeroes = 60;
+ if (n >> 32) zeroes -= 32, n >>= 32;
+ if (n >> 16) zeroes -= 16, n >>= 16;
+ if (n >> 8) zeroes -= 8, n >>= 8;
+ if (n >> 4) zeroes -= 4, n >>= 4;
+ return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0\0"[n] + zeroes;
+#endif
+}
+
+} // namespace strings_internal
+} // namespace absl
+
+#endif // S2_THIRD_PARTY_ABSL_STRINGS_INTERNAL_BITS_H_
--- /dev/null
+//
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// These routines provide mem versions of standard C string routines,
+// such as strpbrk. They function exactly the same as the str versions,
+// so if you wonder what they are, replace the word "mem" by
+// "str" and check out the man page. I could return void*, as the
+// strutil.h mem*() routines tend to do, but I return char* instead
+// since this is by far the most common way these functions are called.
+//
+// The difference between the mem and str versions is the mem version
+// takes a pointer and a length, rather than a '\0'-terminated string.
+// The memcase* routines defined here assume the locale is "C"
+// (they use absl::ascii_tolower instead of tolower).
+//
+// These routines are based on the BSD library.
+//
+// Here's a list of routines from string.h, and their mem analogues.
+// Functions in lowercase are defined in string.h; those in UPPERCASE
+// are defined here:
+//
+// strlen --
+// strcat strncat MEMCAT
+// strcpy strncpy memcpy
+// -- memccpy (very cool function, btw)
+// -- memmove
+// -- memset
+// strcmp strncmp memcmp
+// strcasecmp strncasecmp MEMCASECMP
+// strchr memchr
+// strcoll --
+// strxfrm --
+// strdup strndup MEMDUP
+// strrchr MEMRCHR
+// strspn MEMSPN
+// strcspn MEMCSPN
+// strpbrk MEMPBRK
+// strstr MEMSTR MEMMEM
+// (g)strcasestr MEMCASESTR MEMCASEMEM
+// strtok --
+// strprefix MEMPREFIX (strprefix is from strutil.h)
+// strcaseprefix MEMCASEPREFIX (strcaseprefix is from strutil.h)
+// strsuffix MEMSUFFIX (strsuffix is from strutil.h)
+// strcasesuffix MEMCASESUFFIX (strcasesuffix is from strutil.h)
+// -- MEMIS
+// -- MEMCASEIS
+// strcount MEMCOUNT (strcount is from strutil.h)
+
+#ifndef S2_THIRD_PARTY_ABSL_STRINGS_INTERNAL_MEMUTIL_H_
+#define S2_THIRD_PARTY_ABSL_STRINGS_INTERNAL_MEMUTIL_H_
+
+#include <cstddef>
+#include <cstring>
+
+#include "s2/third_party/absl/base/port.h" // disable some warnings on Windows
+#include "s2/third_party/absl/strings/ascii.h" // for absl::ascii_tolower
+
+namespace absl {
+namespace strings_internal {
+
+inline char* memcat(char* dest, size_t destlen, const char* src,
+ size_t srclen) {
+ return reinterpret_cast<char*>(memcpy(dest + destlen, src, srclen));
+}
+
+int memcasecmp(const char* s1, const char* s2, size_t len);
+char* memdup(const char* s, size_t slen);
+char* memrchr(const char* s, int c, size_t slen);
+size_t memspn(const char* s, size_t slen, const char* accept);
+size_t memcspn(const char* s, size_t slen, const char* reject);
+char* mempbrk(const char* s, size_t slen, const char* accept);
+
+// This is for internal use only. Don't call this directly
+template <bool case_sensitive>
+const char* int_memmatch(const char* haystack, size_t haylen,
+ const char* needle, size_t neelen) {
+ if (0 == neelen) {
+ return haystack; // even if haylen is 0
+ }
+ const char* hayend = haystack + haylen;
+ const char* needlestart = needle;
+ const char* needleend = needlestart + neelen;
+
+ for (; haystack < hayend; ++haystack) {
+ char hay = case_sensitive
+ ? *haystack
+ : absl::ascii_tolower(static_cast<unsigned char>(*haystack));
+ char nee = case_sensitive
+ ? *needle
+ : absl::ascii_tolower(static_cast<unsigned char>(*needle));
+ if (hay == nee) {
+ if (++needle == needleend) {
+ return haystack + 1 - neelen;
+ }
+ } else if (needle != needlestart) {
+ // must back up haystack in case a prefix matched (find "aab" in "aaab")
+ haystack -= needle - needlestart; // for loop will advance one more
+ needle = needlestart;
+ }
+ }
+ return nullptr;
+}
+
+// These are the guys you can call directly
+inline const char* memstr(const char* phaystack, size_t haylen,
+ const char* pneedle) {
+ return int_memmatch<true>(phaystack, haylen, pneedle, strlen(pneedle));
+}
+
+inline const char* memcasestr(const char* phaystack, size_t haylen,
+ const char* pneedle) {
+ return int_memmatch<false>(phaystack, haylen, pneedle, strlen(pneedle));
+}
+
+inline const char* memmem(const char* phaystack, size_t haylen,
+ const char* pneedle, size_t needlelen) {
+ return int_memmatch<true>(phaystack, haylen, pneedle, needlelen);
+}
+
+inline const char* memcasemem(const char* phaystack, size_t haylen,
+ const char* pneedle, size_t needlelen) {
+ return int_memmatch<false>(phaystack, haylen, pneedle, needlelen);
+}
+
+// This is significantly faster for case-sensitive matches with very
+// few possible matches. See unit test for benchmarks.
+const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle,
+ size_t neelen);
+
+} // namespace strings_internal
+} // namespace absl
+
+#endif // S2_THIRD_PARTY_ABSL_STRINGS_INTERNAL_MEMUTIL_H_
--- /dev/null
+//
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef S2_THIRD_PARTY_ABSL_STRINGS_INTERNAL_RESIZE_UNINITIALIZED_H_
+#define S2_THIRD_PARTY_ABSL_STRINGS_INTERNAL_RESIZE_UNINITIALIZED_H_
+
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include "s2/third_party/absl/base/port.h"
+#include "s2/third_party/absl/meta/type_traits.h" // for void_t
+
+namespace absl {
+namespace strings_internal {
+
+// Is a subclass of true_type or false_type, depending on whether or not
+// T has a __resize_default_init member.
+template <typename string_type, typename = void>
+struct ResizeUninitializedTraits {
+ using HasMember = std::false_type;
+ static void Resize(string_type* s, size_t new_size) { s->resize(new_size); }
+};
+
+// __resize_default_init is provided by libc++ >= 8.0 and by Google's internal
+// ::string implementation.
+template <typename string_type>
+struct ResizeUninitializedTraits<
+ string_type, absl::void_t<decltype(std::declval<string_type&>()
+ .__resize_default_init(237))> > {
+ using HasMember = std::true_type;
+ static void Resize(string_type* s, size_t new_size) {
+ s->__resize_default_init(new_size);
+ }
+};
+
+// Returns true if the string implementation supports a resize where
+// the new characters added to the string are left untouched.
+//
+// (A better name might be "STLStringSupportsUninitializedResize", alluding to
+// the previous function.)
+template <typename string_type>
+inline constexpr bool STLStringSupportsNontrashingResize(string_type*) {
+ return ResizeUninitializedTraits<string_type>::HasMember::value;
+}
+
+// Like str->resize(new_size), except any new characters added to "*str" as a
+// result of resizing may be left uninitialized, rather than being filled with
+// '0' bytes. Typically used when code is then going to overwrite the backing
+// store of the string with known data. Uses a Google extension to ::string.
+template <typename string_type, typename = void>
+inline void STLStringResizeUninitialized(string_type* s, size_t new_size) {
+ ResizeUninitializedTraits<string_type>::Resize(s, new_size);
+}
+
+} // namespace strings_internal
+} // namespace absl
+
+#endif // S2_THIRD_PARTY_ABSL_STRINGS_INTERNAL_RESIZE_UNINITIALIZED_H_
--- /dev/null
+//
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: match.h
+// -----------------------------------------------------------------------------
+//
+// This file contains simple utilities for performing string matching checks.
+// All of these function parameters are specified as `absl::string_view`,
+// meaning that these functions can accept `std::string`, `absl::string_view` or
+// nul-terminated C-style strings.
+//
+// Examples:
+// std::string s = "foo";
+// absl::string_view sv = "f";
+// assert(absl::StrContains(s, sv));
+//
+// Note: The order of parameters in these functions is designed to mimic the
+// order an equivalent member function would exhibit;
+// e.g. `s.Contains(x)` ==> `absl::StrContains(s, x).
+#ifndef S2_THIRD_PARTY_ABSL_STRINGS_MATCH_H_
+#define S2_THIRD_PARTY_ABSL_STRINGS_MATCH_H_
+
+#include <cstring>
+
+#include "s2/third_party/absl/strings/string_view.h"
+
+namespace absl {
+
+// StrContains()
+//
+// Returns whether a given string `haystack` contains the substring `needle`.
+inline bool StrContains(absl::string_view haystack, absl::string_view needle) {
+ return haystack.find(needle, 0) != haystack.npos;
+}
+
+// StartsWith()
+//
+// Returns whether a given string `text` begins with `prefix`.
+inline bool StartsWith(absl::string_view text, absl::string_view prefix) {
+ return prefix.empty() ||
+ (text.size() >= prefix.size() &&
+ 0 == std::memcmp(text.data(), prefix.data(), prefix.size()));
+}
+
+// EndsWith()
+//
+// Returns whether a given string `text` ends with `suffix`.
+inline bool EndsWith(absl::string_view text, absl::string_view suffix) {
+ return suffix.empty() ||
+ (text.size() >= suffix.size() &&
+ 0 == std::memcmp(text.data() + (text.size() - suffix.size()),
+ suffix.data(), suffix.size())
+ );
+}
+
+// EqualsIgnoreCase()
+//
+// Returns whether given ASCII strings `piece1` and `piece2` are equal, ignoring
+// case in the comparison.
+bool EqualsIgnoreCase(absl::string_view piece1, absl::string_view piece2);
+
+// StartsWithIgnoreCase()
+//
+// Returns whether a given ASCII string `text` starts with `prefix`,
+// ignoring case in the comparison.
+bool StartsWithIgnoreCase(absl::string_view text, absl::string_view prefix);
+
+// EndsWithIgnoreCase()
+//
+// Returns whether a given ASCII string `text` ends with `suffix`, ignoring
+// case in the comparison.
+bool EndsWithIgnoreCase(absl::string_view text, absl::string_view suffix);
+
+} // namespace absl
+
+#endif // S2_THIRD_PARTY_ABSL_STRINGS_MATCH_H_
--- /dev/null
+//
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: numbers.h
+// -----------------------------------------------------------------------------
+//
+// This package contains functions for converting strings to numbers. For
+// converting numbers to strings, use `StrCat()` or `StrAppend()` in str_cat.h,
+// which automatically detect and convert most number values appropriately.
+
+#ifndef S2_THIRD_PARTY_ABSL_STRINGS_NUMBERS_H_
+#define S2_THIRD_PARTY_ABSL_STRINGS_NUMBERS_H_
+
+#include <cstddef>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+#include <limits>
+#include <string>
+#include <type_traits>
+
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/third_party/absl/base/port.h"
+#include "s2/third_party/absl/numeric/int128.h"
+#include "s2/third_party/absl/strings/string_view.h"
+
+namespace absl {
+
+// SimpleAtoi()
+//
+// Converts the given string into an integer value, returning `true` if
+// successful. The string must reflect a base-10 integer (optionally followed or
+// preceded by ASCII whitespace) whose value falls within the range of the
+// integer type,
+template <typename int_type>
+ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view s, int_type* out);
+
+// SimpleAtof()
+//
+// Converts the given string (optionally followed or preceded by ASCII
+// whitespace) into a float, which may be rounded on overflow or underflow.
+// See http://en.cppreference.com/w/c/string/byte/strtof for details about the
+// allowed formats for `str`.
+ABSL_MUST_USE_RESULT bool SimpleAtof(absl::string_view str, float* value);
+
+// SimpleAtod()
+//
+// Converts the given string (optionally followed or preceded by ASCII
+// whitespace) into a double, which may be rounded on overflow or underflow.
+// See http://en.cppreference.com/w/c/string/byte/strtof for details about the
+// allowed formats for `str`.
+ABSL_MUST_USE_RESULT bool SimpleAtod(absl::string_view str, double* value);
+
+// SimpleAtob()
+//
+// Converts the given string into a boolean, returning `true` if successful.
+// The following case-insensitive strings are interpreted as boolean `true`:
+// "true", "t", "yes", "y", "1". The following case-insensitive strings
+// are interpreted as boolean `false`: "false", "f", "no", "n", "0".
+ABSL_MUST_USE_RESULT bool SimpleAtob(absl::string_view str, bool* value);
+
+} // namespace absl
+
+// End of public API. Implementation details follow.
+
+namespace absl {
+namespace numbers_internal {
+
+// safe_strto?() functions for implementing SimpleAtoi()
+bool safe_strto32_base(absl::string_view text, int32_t* value, int base);
+bool safe_strto64_base(absl::string_view text, int64_t* value, int base);
+bool safe_strtou32_base(absl::string_view text, uint32_t* value, int base);
+bool safe_strtou64_base(absl::string_view text, uint64_t* value, int base);
+
+static const int kFastToBufferSize = 32;
+static const int kSixDigitsToBufferSize = 16;
+
+
+// Helper function for fast formatting of floating-point values.
+// The result is the same as printf's "%g", a.k.a. "%.6g"; that is, six
+// significant digits are returned, trailing zeros are removed, and numbers
+// outside the range 0.0001-999999 are output using scientific notation
+// (1.23456e+06). This routine is heavily optimized.
+// Required buffer size is `kSixDigitsToBufferSize`.
+size_t SixDigitsToBuffer(double d, char* buffer);
+
+// These functions are intended for speed. All functions take an output buffer
+// as an argument and return a pointer to the last byte they wrote, which is the
+// terminating '\0'. At most `kFastToBufferSize` bytes are written.
+char* FastIntToBuffer(int32_t, char*);
+char* FastIntToBuffer(uint32_t, char*);
+char* FastIntToBuffer(int64_t, char*);
+char* FastIntToBuffer(uint64_t, char*);
+
+// For enums and integer types that are not an exact match for the types above,
+// use templates to call the appropriate one of the four overloads above.
+template <typename int_type>
+char* FastIntToBuffer(int_type i, char* buffer) {
+ static_assert(sizeof(i) <= 64 / 8,
+ "FastIntToBuffer works only with 64-bit-or-less integers.");
+ // TODO(user): This signed-ness check is used because it works correctly
+ // with enums, and it also serves to check that int_type is not a pointer.
+ // If one day something like std::is_signed<enum E> works, switch to it.
+ if (static_cast<int_type>(1) - 2 < 0) { // Signed
+ if (sizeof(i) > 32 / 8) { // 33-bit to 64-bit
+ return FastIntToBuffer(static_cast<int64_t>(i), buffer);
+ } else { // 32-bit or less
+ return FastIntToBuffer(static_cast<int32_t>(i), buffer);
+ }
+ } else { // Unsigned
+ if (sizeof(i) > 32 / 8) { // 33-bit to 64-bit
+ return FastIntToBuffer(static_cast<uint64_t>(i), buffer);
+ } else { // 32-bit or less
+ return FastIntToBuffer(static_cast<uint32_t>(i), buffer);
+ }
+ }
+}
+
+// Implementation of SimpleAtoi, generalized to support arbitrary base (used
+// with base different from 10 elsewhere in Abseil implementation).
+template <typename int_type>
+ABSL_MUST_USE_RESULT bool safe_strtoi_base(absl::string_view s, int_type* out,
+ int base) {
+ static_assert(sizeof(*out) == 4 || sizeof(*out) == 8,
+ "SimpleAtoi works only with 32-bit or 64-bit integers.");
+ static_assert(!std::is_floating_point<int_type>::value,
+ "Use SimpleAtof or SimpleAtod instead.");
+ bool parsed;
+ // TODO(user): This signed-ness check is used because it works correctly
+ // with enums, and it also serves to check that int_type is not a pointer.
+ // If one day something like std::is_signed<enum E> works, switch to it.
+ if (static_cast<int_type>(1) - 2 < 0) { // Signed
+ if (sizeof(*out) == 64 / 8) { // 64-bit
+ int64_t val;
+ parsed = numbers_internal::safe_strto64_base(s, &val, base);
+ *out = static_cast<int_type>(val);
+ } else { // 32-bit
+ int32_t val;
+ parsed = numbers_internal::safe_strto32_base(s, &val, base);
+ *out = static_cast<int_type>(val);
+ }
+ } else { // Unsigned
+ if (sizeof(*out) == 64 / 8) { // 64-bit
+ uint64_t val;
+ parsed = numbers_internal::safe_strtou64_base(s, &val, base);
+ *out = static_cast<int_type>(val);
+ } else { // 32-bit
+ uint32_t val;
+ parsed = numbers_internal::safe_strtou32_base(s, &val, base);
+ *out = static_cast<int_type>(val);
+ }
+ }
+ return parsed;
+}
+
+} // namespace numbers_internal
+
+// SimpleAtoi()
+//
+// Converts a string to an integer, using `safe_strto?()` functions for actual
+// parsing, returning `true` if successful. The `safe_strto?()` functions apply
+// strict checking; the string must be a base-10 integer, optionally followed or
+// preceded by ASCII whitespace, with a value in the range of the corresponding
+// integer type.
+template <typename int_type>
+ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view s, int_type* out) {
+ return numbers_internal::safe_strtoi_base(s, out, 10);
+}
+
+} // namespace absl
+
+
+
+#endif // S2_THIRD_PARTY_ABSL_STRINGS_NUMBERS_H_
--- /dev/null
+//
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: str_cat.h
+// -----------------------------------------------------------------------------
+//
+// This package contains functions for efficiently concatenating and appending
+// strings: `StrCat()` and `StrAppend()`. Most of the work within these routines
+// is actually handled through use of a special AlphaNum type, which was
+// designed to be used as a parameter type that efficiently manages conversion
+// to strings and avoids copies in the above operations.
+//
+// Any routine accepting either a string or a number may accept `AlphaNum`.
+// The basic idea is that by accepting a `const AlphaNum &` as an argument
+// to your function, your callers will automagically convert bools, integers,
+// and floating point values to strings for you.
+//
+// NOTE: Use of `AlphaNum` outside of the //absl/strings package is unsupported
+// except for the specific case of function parameters of type `AlphaNum` or
+// `const AlphaNum &`. In particular, instantiating `AlphaNum` directly as a
+// stack variable is not supported.
+//
+// Conversion from 8-bit values is not accepted because, if it were, then an
+// attempt to pass ':' instead of ":" might result in a 58 ending up in your
+// result.
+//
+// Bools convert to "0" or "1".
+//
+// Floating point numbers are formatted with six-digit precision, which is
+// the default for "std::cout <<" or printf "%g" (the same as "%.6g").
+//
+//
+// You can convert to hexadecimal output rather than decimal output using the
+// `Hex` type contained here. To do so, pass `Hex(my_int)` as a parameter to
+// `StrCat()` or `StrAppend()`. You may specify a minimum hex field width using
+// a `PadSpec` enum.
+//
+// -----------------------------------------------------------------------------
+
+#ifndef S2_THIRD_PARTY_ABSL_STRINGS_STR_CAT_H_
+#define S2_THIRD_PARTY_ABSL_STRINGS_STR_CAT_H_
+
+#include <array>
+#include <cstdint>
+#include <string>
+#include <type_traits>
+
+#include "s2/third_party/absl/base/port.h"
+#include "s2/third_party/absl/strings/numbers.h"
+#include "s2/third_party/absl/strings/string_view.h"
+
+namespace absl {
+
+namespace strings_internal {
+// AlphaNumBuffer allows a way to pass a string to StrCat without having to do
+// memory allocation. It is simply a pair of a fixed-size character array, and
+// a size. Please don't use outside of absl, yet.
+template <size_t max_size>
+struct AlphaNumBuffer {
+ std::array<char, max_size> data;
+ size_t size;
+};
+
+} // namespace strings_internal
+
+// Enum that specifies the number of significant digits to return in a `Hex` or
+// `Dec` conversion and fill character to use. A `kZeroPad2` value, for example,
+// would produce hexadecimal strings such as "0A","0F" and a 'kSpacePad5' value
+// would produce hexadecimal strings such as " A"," F".
+enum PadSpec : uint8_t {
+ kNoPad = 1,
+ kZeroPad2,
+ kZeroPad3,
+ kZeroPad4,
+ kZeroPad5,
+ kZeroPad6,
+ kZeroPad7,
+ kZeroPad8,
+ kZeroPad9,
+ kZeroPad10,
+ kZeroPad11,
+ kZeroPad12,
+ kZeroPad13,
+ kZeroPad14,
+ kZeroPad15,
+ kZeroPad16,
+ kZeroPad17,
+ kZeroPad18,
+ kZeroPad19,
+ kZeroPad20,
+
+ kSpacePad2 = kZeroPad2 + 64,
+ kSpacePad3,
+ kSpacePad4,
+ kSpacePad5,
+ kSpacePad6,
+ kSpacePad7,
+ kSpacePad8,
+ kSpacePad9,
+ kSpacePad10,
+ kSpacePad11,
+ kSpacePad12,
+ kSpacePad13,
+ kSpacePad14,
+ kSpacePad15,
+ kSpacePad16,
+ kSpacePad17,
+ kSpacePad18,
+ kSpacePad19,
+ kSpacePad20,
+};
+
+// -----------------------------------------------------------------------------
+// Hex
+// -----------------------------------------------------------------------------
+//
+// `Hex` stores a set of hexadecimal string conversion parameters for use
+// within `AlphaNum` string conversions.
+struct Hex {
+ uint64_t value;
+ uint8_t width;
+ char fill;
+
+ template <typename Int>
+ explicit Hex(
+ Int v, PadSpec spec = absl::kNoPad,
+ typename std::enable_if<sizeof(Int) == 1 &&
+ !std::is_pointer<Int>::value>::type* = nullptr)
+ : Hex(spec, static_cast<uint8_t>(v)) {}
+ template <typename Int>
+ explicit Hex(
+ Int v, PadSpec spec = absl::kNoPad,
+ typename std::enable_if<sizeof(Int) == 2 &&
+ !std::is_pointer<Int>::value>::type* = nullptr)
+ : Hex(spec, static_cast<uint16_t>(v)) {}
+ template <typename Int>
+ explicit Hex(
+ Int v, PadSpec spec = absl::kNoPad,
+ typename std::enable_if<sizeof(Int) == 4 &&
+ !std::is_pointer<Int>::value>::type* = nullptr)
+ : Hex(spec, static_cast<uint32_t>(v)) {}
+ template <typename Int>
+ explicit Hex(
+ Int v, PadSpec spec = absl::kNoPad,
+ typename std::enable_if<sizeof(Int) == 8 &&
+ !std::is_pointer<Int>::value>::type* = nullptr)
+ : Hex(spec, static_cast<uint64_t>(v)) {}
+ template <typename Pointee>
+ explicit Hex(Pointee* v, PadSpec spec = absl::kNoPad)
+ : Hex(spec, reinterpret_cast<uintptr_t>(v)) {}
+
+ private:
+ Hex(PadSpec spec, uint64_t v)
+ : value(v),
+ width(spec == absl::kNoPad
+ ? 1
+ : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
+ : spec - absl::kZeroPad2 + 2),
+ fill(spec >= absl::kSpacePad2 ? ' ' : '0') {}
+};
+
+// -----------------------------------------------------------------------------
+// Dec
+// -----------------------------------------------------------------------------
+//
+// `Dec` stores a set of decimal string conversion parameters for use
+// within `AlphaNum` string conversions. Dec is slower than the default
+// integer conversion, so use it only if you need padding.
+struct Dec {
+ uint64_t value;
+ uint8_t width;
+ char fill;
+ bool neg;
+
+ template <typename Int>
+ explicit Dec(Int v, PadSpec spec = absl::kNoPad,
+ typename std::enable_if<(sizeof(Int) <= 8)>::type* = nullptr)
+ : value(v >= 0 ? static_cast<uint64_t>(v)
+ : uint64_t{0} - static_cast<uint64_t>(v)),
+ width(spec == absl::kNoPad
+ ? 1
+ : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
+ : spec - absl::kZeroPad2 + 2),
+ fill(spec >= absl::kSpacePad2 ? ' ' : '0'),
+ neg(v < 0) {}
+};
+
+// -----------------------------------------------------------------------------
+// AlphaNum
+// -----------------------------------------------------------------------------
+//
+// The `AlphaNum` class acts as the main parameter type for `StrCat()` and
+// `StrAppend()`, providing efficient conversion of numeric, boolean, and
+// hexadecimal values (through the `Hex` type) into strings.
+
+class AlphaNum {
+ public:
+ // No bool ctor -- bools convert to an integral type.
+ // A bool ctor would also convert incoming pointers (bletch).
+
+ AlphaNum(int x) // NOLINT(runtime/explicit)
+ : piece_(digits_,
+ numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
+ AlphaNum(unsigned int x) // NOLINT(runtime/explicit)
+ : piece_(digits_,
+ numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
+ AlphaNum(long x) // NOLINT(*)
+ : piece_(digits_,
+ numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
+ AlphaNum(unsigned long x) // NOLINT(*)
+ : piece_(digits_,
+ numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
+ AlphaNum(long long x) // NOLINT(*)
+ : piece_(digits_,
+ numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
+ AlphaNum(unsigned long long x) // NOLINT(*)
+ : piece_(digits_,
+ numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {}
+
+ AlphaNum(float f) // NOLINT(runtime/explicit)
+ : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
+ AlphaNum(double f) // NOLINT(runtime/explicit)
+ : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {}
+
+ AlphaNum(Hex hex); // NOLINT(runtime/explicit)
+ AlphaNum(Dec dec); // NOLINT(runtime/explicit)
+
+ template <size_t size>
+ AlphaNum( // NOLINT(runtime/explicit)
+ const strings_internal::AlphaNumBuffer<size>& buf)
+ : piece_(&buf.data[0], buf.size) {}
+
+ AlphaNum(const char* c_str) : piece_(c_str) {} // NOLINT(runtime/explicit)
+ AlphaNum(absl::string_view pc) : piece_(pc) {} // NOLINT(runtime/explicit)
+
+ template <typename Allocator>
+ AlphaNum( // NOLINT(runtime/explicit)
+ const std::basic_string<char, std::char_traits<char>, Allocator>& str)
+ : piece_(str) {}
+
+ // Use string literals ":" instead of character literals ':'.
+ AlphaNum(char c) = delete; // NOLINT(runtime/explicit)
+
+ AlphaNum(const AlphaNum&) = delete;
+ AlphaNum& operator=(const AlphaNum&) = delete;
+
+ absl::string_view::size_type size() const { return piece_.size(); }
+ const char* data() const { return piece_.data(); }
+ absl::string_view Piece() const { return piece_; }
+
+ // Normal enums are already handled by the integer formatters.
+ // This overload matches only scoped enums.
+ template <typename T,
+ typename = typename std::enable_if<
+ std::is_enum<T>{} && !std::is_convertible<T, int>{}>::type>
+ AlphaNum(T e) // NOLINT(runtime/explicit)
+ : AlphaNum(static_cast<typename std::underlying_type<T>::type>(e)) {}
+
+ private:
+ absl::string_view piece_;
+ char digits_[numbers_internal::kFastToBufferSize];
+};
+
+// -----------------------------------------------------------------------------
+// StrCat()
+// -----------------------------------------------------------------------------
+//
+// Merges given strings or numbers, using no delimiter(s).
+//
+// `StrCat()` is designed to be the fastest possible way to construct a string
+// out of a mix of raw C strings, string_views, strings, bool values,
+// and numeric values.
+//
+// Don't use `StrCat()` for user-visible strings. The localization process
+// works poorly on strings built up out of fragments.
+//
+// For clarity and performance, don't use `StrCat()` when appending to a
+// string. Use `StrAppend()` instead. In particular, avoid using any of these
+// (anti-)patterns:
+//
+// str.append(StrCat(...))
+// str += StrCat(...)
+// str = StrCat(str, ...)
+//
+// The last case is the worst, with a potential to change a loop
+// from a linear time operation with O(1) dynamic allocations into a
+// quadratic time operation with O(n) dynamic allocations.
+//
+// See `StrAppend()` below for more information.
+
+namespace strings_internal {
+
+// Do not call directly - this is not part of the public API.
+string CatPieces(std::initializer_list<absl::string_view> pieces);
+void AppendPieces(string* dest,
+ std::initializer_list<absl::string_view> pieces);
+
+} // namespace strings_internal
+
+ABSL_MUST_USE_RESULT inline string StrCat() { return string(); }
+
+ABSL_MUST_USE_RESULT inline string StrCat(const AlphaNum& a) {
+ return string(a.data(), a.size());
+}
+
+ABSL_MUST_USE_RESULT string StrCat(const AlphaNum& a, const AlphaNum& b);
+ABSL_MUST_USE_RESULT string StrCat(const AlphaNum& a, const AlphaNum& b,
+ const AlphaNum& c);
+ABSL_MUST_USE_RESULT string StrCat(const AlphaNum& a, const AlphaNum& b,
+ const AlphaNum& c, const AlphaNum& d);
+
+// Support 5 or more arguments
+template <typename... AV>
+ABSL_MUST_USE_RESULT inline string StrCat(const AlphaNum& a, const AlphaNum& b,
+ const AlphaNum& c, const AlphaNum& d,
+ const AlphaNum& e,
+ const AV&... args) {
+ return strings_internal::CatPieces(
+ {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
+ static_cast<const AlphaNum&>(args).Piece()...});
+}
+
+// -----------------------------------------------------------------------------
+// StrAppend()
+// -----------------------------------------------------------------------------
+//
+// Appends a string or set of strings to an existing string, in a similar
+// fashion to `StrCat()`.
+//
+// WARNING: `StrAppend(&str, a, b, c, ...)` requires that none of the
+// a, b, c, parameters be a reference into str. For speed, `StrAppend()` does
+// not try to check each of its input arguments to be sure that they are not
+// a subset of the string being appended to. That is, while this will work:
+//
+// string s = "foo";
+// s += s;
+//
+// This output is undefined:
+//
+// string s = "foo";
+// StrAppend(&s, s);
+//
+// This output is undefined as well, since `absl::string_view` does not own its
+// data:
+//
+// string s = "foobar";
+// absl::string_view p = s;
+// StrAppend(&s, p);
+
+inline void StrAppend(string*) {}
+void StrAppend(string* dest, const AlphaNum& a);
+void StrAppend(string* dest, const AlphaNum& a, const AlphaNum& b);
+void StrAppend(string* dest, const AlphaNum& a, const AlphaNum& b,
+ const AlphaNum& c);
+void StrAppend(string* dest, const AlphaNum& a, const AlphaNum& b,
+ const AlphaNum& c, const AlphaNum& d);
+
+// Support 5 or more arguments
+template <typename... AV>
+inline void StrAppend(string* dest, const AlphaNum& a, const AlphaNum& b,
+ const AlphaNum& c, const AlphaNum& d, const AlphaNum& e,
+ const AV&... args) {
+ strings_internal::AppendPieces(
+ dest, {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
+ static_cast<const AlphaNum&>(args).Piece()...});
+}
+
+
+// Helper function for the future StrCat default floating-point format, %.6g
+// This is fast.
+inline strings_internal::AlphaNumBuffer<
+ numbers_internal::kSixDigitsToBufferSize>
+SixDigits(double d) {
+ strings_internal::AlphaNumBuffer<numbers_internal::kSixDigitsToBufferSize>
+ result;
+ result.size = numbers_internal::SixDigitsToBuffer(d, &result.data[0]);
+ return result;
+}
+
+
+
+
+} // namespace absl
+
+#endif // S2_THIRD_PARTY_ABSL_STRINGS_STR_CAT_H_
--- /dev/null
+#ifndef S2_THIRD_PARTY_ABSL_STRINGS_STR_JOIN_H_
+#define S2_THIRD_PARTY_ABSL_STRINGS_STR_JOIN_H_
+
+#include <string>
+
+namespace absl {
+
+template <typename Range>
+std::string StrJoin(const Range& strs, const char* delim) {
+ std::string joined;
+ bool first = true;
+ for (const auto& s : strs) {
+ if (!first) joined.append(delim);
+ joined.append(s);
+ first = false;
+ }
+ return joined;
+}
+
+} // namespace absl
+
+#endif // S2_THIRD_PARTY_ABSL_STRINGS_STR_JOIN_H_
--- /dev/null
+#ifndef S2_THIRD_PARTY_ABSL_STRINGS_STR_SPLIT_H_
+#define S2_THIRD_PARTY_ABSL_STRINGS_STR_SPLIT_H_
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#include "s2/third_party/absl/strings/string_view.h"
+#include "s2/third_party/absl/strings/strip.h"
+
+namespace absl {
+
+template <typename String>
+std::vector<String> StrSplit(
+ String const& text, char delim,
+ std::function<bool(string_view)> predicate);
+template <typename String>
+std::vector<String> StrSplit(String const& text, char delim);
+
+// Returns false if the given StringPiece is empty, indicating that the
+// StrSplit() API should omit the empty string.
+//
+// std::vector<string> v = StrSplit(" a , ,,b,", ',', SkipEmpty());
+// EXPECT_THAT(v, ElementsAre(" a ", " ", "b"));
+struct SkipEmpty {
+ bool operator()(string_view sv) const { return !sv.empty(); }
+};
+
+// Returns false if the given string is empty or contains only whitespace,
+// indicating that the StrSplit() API should omit the string.
+//
+// std::vector<string> v = StrSplit(" a , ,,b,", ',', SkipWhitespace());
+// EXPECT_THAT(v, ElementsAre(" a ", "b"));
+struct SkipWhitespace {
+ bool operator()(string_view sv) const {
+ StripWhitespace(&sv);
+ return !sv.empty();
+ }
+};
+
+} // namespace absl
+
+#endif // S2_THIRD_PARTY_ABSL_STRINGS_STR_SPLIT_H_
--- /dev/null
+//
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: string_view.h
+// -----------------------------------------------------------------------------
+//
+// This file contains the definition of the `absl::string_view` class. A
+// `string_view` points to a contiguous span of characters, often part or all of
+// another `std::string`, double-quoted string literal, character array, or even
+// another `string_view`.
+//
+// This `absl::string_view` abstraction is designed to be a drop-in
+// replacement for the C++17 `std::string_view` abstraction.
+#ifndef S2_THIRD_PARTY_ABSL_STRINGS_STRING_VIEW_H_
+#define S2_THIRD_PARTY_ABSL_STRINGS_STRING_VIEW_H_
+
+#include <algorithm>
+#include "s2/third_party/absl/base/config.h"
+
+#ifdef ABSL_HAVE_STD_STRING_VIEW
+
+#include <string_view>
+
+namespace absl {
+using std::string_view;
+} // namespace absl
+
+#else // ABSL_HAVE_STD_STRING_VIEW
+
+#include <cassert>
+#include <cstddef>
+#include <cstring>
+#include <iosfwd>
+#include <iterator>
+#include <limits>
+#include <string>
+
+#include "s2/third_party/absl/base/internal/throw_delegate.h"
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/third_party/absl/base/port.h"
+
+// string_view has *two* size types.
+// string_view::size_type
+// is unsigned
+// is 32 bits in LP32, 64 bits in LP64, 64 bits in LLP64
+// no future changes intended
+// stringpiece_ssize_type
+// is signed
+// is 32 bits in LP32, 64 bits in LP64, 64 bits in LLP64
+// future changes intended
+//
+typedef string::difference_type stringpiece_ssize_type;
+
+namespace absl {
+
+// absl::string_view
+//
+// A `string_view` provides a lightweight view into the string data provided by
+// a `std::string`, double-quoted string literal, character array, or even
+// another `string_view`. A `string_view` does *not* own the string to which it
+// points, and that data cannot be modified through the view.
+//
+// You can use `string_view` as a function or method parameter anywhere a
+// parameter can receive a double-quoted string literal, `const char*`,
+// `std::string`, or another `absl::string_view` argument with no need to copy
+// the string data. Systematic use of `string_view` within function arguments
+// reduces data copies and `strlen()` calls.
+//
+// Because of its small size, prefer passing `string_view` by value:
+//
+// void MyFunction(absl::string_view arg);
+//
+// If circumstances require, you may also pass one by const reference:
+//
+// void MyFunction(const absl::string_view& arg); // not preferred
+//
+// Passing by value generates slightly smaller code for many architectures.
+//
+// In either case, the source data of the `string_view` must outlive the
+// `string_view` itself.
+// For more discussion, see the thread go/stringpiecebyvalue on c-users.
+//
+// A `string_view` is also suitable for local variables if you know that the
+// lifetime of the underlying object is longer than the lifetime of your
+// `string_view` variable. However, beware of binding a `string_view` to a
+// temporary value:
+//
+// // BAD use of string_view: lifetime problem
+// absl::string_view sv = obj.ReturnAString();
+//
+// // GOOD use of string_view: str outlives sv
+// std::string str = obj.ReturnAString();
+// absl::string_view sv = str;
+//
+// Due to lifetime issues, a `string_view` is sometimes a poor choice for a
+// return value and usually a poor choice for a data member. If you do use a
+// `string_view` this way, it is your responsibility to ensure that the object
+// pointed to by the `string_view` outlives the `string_view`.
+//
+// A `string_view` may represent a whole string or just part of a string. For
+// example, when splitting a string, `std::vector<absl::string_view>` is a
+// natural data type for the output.
+//
+// For another example, a Cord is a non-contiguous, potentially very
+// long string-like object. The Cord class has an interface that iteratively
+// provides string_view objects that point to the successive pieces of a Cord
+// object.
+//
+// When constructed from a source which is nul-terminated, the `string_view`
+// itself will not include the nul-terminator unless a specific size (including
+// the nul) is passed to the constructor. As a result, common idioms that work
+// on nul-terminated strings do not work on `string_view` objects. If you write
+// code that scans a `string_view`, you must check its length rather than test
+// for nul, for example. Note, however, that nuls may still be embedded within
+// a `string_view` explicitly.
+//
+// You may create a null `string_view` in two ways:
+//
+// absl::string_view sv();
+// absl::string_view sv(nullptr, 0);
+//
+// For the above, `sv.data() == nullptr`, `sv.length() == 0`, and
+// `sv.empty() == true`. Also, if you create a `string_view` with a non-null
+// pointer then `sv.data() != nullptr`. Thus, you can use `string_view()` to
+// signal an undefined value that is different from other `string_view` values
+// in a similar fashion to how `const char* p1 = nullptr;` is different from
+// `const char* p2 = "";`. However, in practice, it is not recommended to rely
+// on this behavior.
+//
+// Be careful not to confuse a null `string_view` with an empty one. A null
+// `string_view` is an empty `string_view`, but some empty `string_view`s are
+// not null. Prefer checking for emptiness over checking for null.
+//
+// There are many ways to create an empty string_view:
+//
+// const char* nullcp = nullptr;
+// // string_view.size() will return 0 in all cases.
+// absl::string_view();
+// absl::string_view(nullcp, 0);
+// absl::string_view("");
+// absl::string_view("", 0);
+// absl::string_view("abcdef", 0);
+// absl::string_view("abcdef" + 6, 0);
+//
+// All empty `string_view` objects whether null or not, are equal:
+//
+// absl::string_view() == absl::string_view("", 0)
+// absl::string_view(nullptr, 0) == absl::string_view("abcdef"+6, 0)
+class string_view {
+ public:
+ using traits_type = std::char_traits<char>;
+ using value_type = char;
+ using pointer = char*;
+ using const_pointer = const char*;
+ using reference = char&;
+ using const_reference = const char&;
+ using const_iterator = const char*;
+ using iterator = const_iterator;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ using reverse_iterator = const_reverse_iterator;
+ using size_type = size_t;
+ using difference_type = std::ptrdiff_t;
+
+ static constexpr size_type npos = static_cast<size_type>(-1);
+
+ // Null `string_view` constructor
+ constexpr string_view() noexcept : ptr_(nullptr), length_(0) {}
+
+ // Implicit constructors
+
+ template <typename Allocator>
+ string_view( // NOLINT(runtime/explicit)
+ const std::basic_string<char, std::char_traits<char>, Allocator>&
+ str) noexcept
+ : ptr_(str.data()), length_(CheckLengthInternal(str.size())) {}
+#if defined(HAS_GLOBAL_STRING)
+ template <typename Allocator>
+ string_view( // NOLINT(runtime/explicit)
+ const basic_string<char, std::char_traits<char>, Allocator>& str) noexcept
+ : ptr_(str.data()), length_(CheckLengthInternal(str.size())) {}
+#endif
+
+ // Implicit constructor of a `string_view` from nul-terminated `str`. When
+ // accepting possibly null strings, use `absl::NullSafeStringView(str)`
+ // instead (see below).
+#if ABSL_HAVE_BUILTIN(__builtin_strlen) || \
+ (defined(__GNUC__) && !defined(__clang__))
+ // GCC has __builtin_strlen according to
+ // https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Other-Builtins.html, but
+ // ABSL_HAVE_BUILTIN doesn't detect that, so we use the extra checks above.
+ // __builtin_strlen is constexpr.
+ constexpr string_view(const char* str) // NOLINT(runtime/explicit)
+ : ptr_(str),
+ length_(CheckLengthInternal(str ? __builtin_strlen(str) : 0)) {}
+#else
+ constexpr string_view(const char* str) // NOLINT(runtime/explicit)
+ : ptr_(str), length_(CheckLengthInternal(str ? strlen(str) : 0)) {}
+#endif
+
+ // Implicit constructor of a `string_view` from a `const char*` and length.
+ constexpr string_view(const char* data, size_type len)
+ : ptr_(data), length_(CheckLengthInternal(len)) {}
+
+ // NOTE: Harmlessly omitted to work around gdb bug.
+ // constexpr string_view(const string_view&) noexcept = default;
+ // string_view& operator=(const string_view&) noexcept = default;
+
+ // Iterators
+
+ // string_view::begin()
+ //
+ // Returns an iterator pointing to the first character at the beginning of the
+ // `string_view`, or `end()` if the `string_view` is empty.
+ constexpr const_iterator begin() const noexcept { return ptr_; }
+
+ // string_view::end()
+ //
+ // Returns an iterator pointing just beyond the last character at the end of
+ // the `string_view`. This iterator acts as a placeholder; attempting to
+ // access it results in undefined behavior.
+ constexpr const_iterator end() const noexcept { return ptr_ + length_; }
+
+ // string_view::cbegin()
+ //
+ // Returns a const iterator pointing to the first character at the beginning
+ // of the `string_view`, or `end()` if the `string_view` is empty.
+ constexpr const_iterator cbegin() const noexcept { return begin(); }
+
+ // string_view::cend()
+ //
+ // Returns a const iterator pointing just beyond the last character at the end
+ // of the `string_view`. This pointer acts as a placeholder; attempting to
+ // access its element results in undefined behavior.
+ constexpr const_iterator cend() const noexcept { return end(); }
+
+ // string_view::rbegin()
+ //
+ // Returns a reverse iterator pointing to the last character at the end of the
+ // `string_view`, or `rend()` if the `string_view` is empty.
+ const_reverse_iterator rbegin() const noexcept {
+ return const_reverse_iterator(end());
+ }
+
+ // string_view::rend()
+ //
+ // Returns a reverse iterator pointing just before the first character at the
+ // beginning of the `string_view`. This pointer acts as a placeholder;
+ // attempting to access its element results in undefined behavior.
+ const_reverse_iterator rend() const noexcept {
+ return const_reverse_iterator(begin());
+ }
+
+ // string_view::crbegin()
+ //
+ // Returns a const reverse iterator pointing to the last character at the end
+ // of the `string_view`, or `crend()` if the `string_view` is empty.
+ const_reverse_iterator crbegin() const noexcept { return rbegin(); }
+
+ // string_view::crend()
+ //
+ // Returns a const reverse iterator pointing just before the first character
+ // at the beginning of the `string_view`. This pointer acts as a placeholder;
+ // attempting to access its element results in undefined behavior.
+ const_reverse_iterator crend() const noexcept { return rend(); }
+
+ // Capacity Utilities
+
+ // string_view::size()
+ //
+ // Returns the number of characters in the `string_view`.
+ constexpr size_type size() const noexcept {
+ return length_;
+ }
+
+ // string_view::length()
+ //
+ // Returns the number of characters in the `string_view`. Alias for `size()`.
+ constexpr size_type length() const noexcept { return size(); }
+
+ // string_view::max_size()
+ //
+ // Returns the maximum number of characters the `string_view` can hold.
+ constexpr size_type max_size() const noexcept { return kMaxSize; }
+
+ // string_view::empty()
+ //
+ // Checks if the `string_view` is empty (refers to no characters).
+ constexpr bool empty() const noexcept { return length_ == 0; }
+
+ // string:view::operator[]
+ //
+ // Returns the ith element of an `string_view` using the array operator.
+ // Note that this operator does not perform any bounds checking.
+ constexpr const_reference operator[](size_type i) const { return ptr_[i]; }
+
+ // string_view::front()
+ //
+ // Returns the first element of a `string_view`.
+ constexpr const_reference front() const { return ptr_[0]; }
+
+ // string_view::back()
+ //
+ // Returns the last element of a `string_view`.
+ constexpr const_reference back() const { return ptr_[size() - 1]; }
+
+ // string_view::data()
+ //
+ // Returns a pointer to the underlying character array (which is of course
+ // stored elsewhere). Note that `string_view::data()` may contain embedded nul
+ // characters, but the returned buffer may or may not be nul-terminated;
+ // therefore, do not pass `data()` to a routine that expects a nul-terminated
+ // string.
+ constexpr const_pointer data() const noexcept { return ptr_; }
+
+ // Modifiers
+
+ // string_view::remove_prefix()
+ //
+ // Removes the first `n` characters from the `string_view`. Note that the
+ // underlying string is not changed, only the view.
+ void remove_prefix(size_type n) {
+ assert(n <= length_);
+ ptr_ += n;
+ length_ -= n;
+ }
+
+ // string_view::remove_suffix()
+ //
+ // Removes the last `n` characters from the `string_view`. Note that the
+ // underlying string is not changed, only the view.
+ void remove_suffix(size_type n) {
+ assert(n <= length_);
+ length_ -= n;
+ }
+
+ // string_view::swap()
+ //
+ // Swaps this `string_view` with another `string_view`.
+ void swap(string_view& s) noexcept {
+ auto t = *this;
+ *this = s;
+ s = t;
+ }
+
+ // Explicit conversion operators
+
+ // Converts to `std::basic_string`.
+ template <typename A>
+ explicit operator std::basic_string<char, traits_type, A>() const {
+ if (!data()) return {};
+ return std::basic_string<char, traits_type, A>(data(), size());
+ }
+#ifdef HAS_GLOBAL_STRING
+ template <typename A>
+ explicit operator ::basic_string<char, traits_type, A>() const {
+ if (!data()) return {};
+ return ::basic_string<char, traits_type, A>(data(), size());
+ }
+#endif // HAS_GLOBAL_STRING
+
+ // string_view::copy()
+ //
+ // Copies the contents of the `string_view` at offset `pos` and length `n`
+ // into `buf`.
+ size_type copy(char* buf, size_type n, size_type pos = 0) const;
+
+ // string_view::substr()
+ //
+ // Returns a "substring" of the `string_view` (at offset `pos` and length
+ // `n`) as another string_view. This function throws `std::out_of_bounds` if
+ // `pos > size`.
+ // Use absl::ClippedSubstr if you need a truncating substr operation.
+ string_view substr(size_type pos, size_type n = npos) const {
+ if (ABSL_PREDICT_FALSE(pos > length_))
+ base_internal::ThrowStdOutOfRange("absl::string_view::substr");
+ n = (std::min)(n, length_ - pos);
+ return string_view(ptr_ + pos, n);
+ }
+
+ // string_view::compare()
+ //
+ // Performs a lexicographical comparison between the `string_view` and
+ // another `absl::string_view`, returning -1 if `this` is less than, 0 if
+ // `this` is equal to, and 1 if `this` is greater than the passed string
+ // view. Note that in the case of data equality, a further comparison is made
+ // on the respective sizes of the two `string_view`s to determine which is
+ // smaller, equal, or greater.
+ int compare(string_view x) const noexcept {
+ auto min_length = (std::min)(length_, x.length_);
+ if (min_length > 0) {
+ int r = memcmp(ptr_, x.ptr_, min_length);
+ if (r < 0) return -1;
+ if (r > 0) return 1;
+ }
+ if (length_ < x.length_) return -1;
+ if (length_ > x.length_) return 1;
+ return 0;
+ }
+
+ // Overload of `string_view::compare()` for comparing a substring of the
+ // 'string_view` and another `absl::string_view`.
+ int compare(size_type pos1, size_type count1, string_view v) const {
+ return substr(pos1, count1).compare(v);
+ }
+
+ // Overload of `string_view::compare()` for comparing a substring of the
+ // `string_view` and a substring of another `absl::string_view`.
+ int compare(size_type pos1, size_type count1, string_view v, size_type pos2,
+ size_type count2) const {
+ return substr(pos1, count1).compare(v.substr(pos2, count2));
+ }
+
+ // Overload of `string_view::compare()` for comparing a `string_view` and a
+ // a different C-style string `s`.
+ int compare(const char* s) const { return compare(string_view(s)); }
+
+ // Overload of `string_view::compare()` for comparing a substring of the
+ // `string_view` and a different string C-style string `s`.
+ int compare(size_type pos1, size_type count1, const char* s) const {
+ return substr(pos1, count1).compare(string_view(s));
+ }
+
+ // Overload of `string_view::compare()` for comparing a substring of the
+ // `string_view` and a substring of a different C-style string `s`.
+ int compare(size_type pos1, size_type count1, const char* s,
+ size_type count2) const {
+ return substr(pos1, count1).compare(string_view(s, count2));
+ }
+
+ // Find Utilities
+
+ // string_view::find()
+ //
+ // Finds the first occurrence of the substring `s` within the `string_view`,
+ // returning the position of the first character's match, or `npos` if no
+ // match was found.
+ size_type find(string_view s, size_type pos = 0) const noexcept;
+
+ // Overload of `string_view::find()` for finding the given character `c`
+ // within the `string_view`.
+ size_type find(char c, size_type pos = 0) const noexcept;
+
+ // string_view::rfind()
+ //
+ // Finds the last occurrence of a substring `s` within the `string_view`,
+ // returning the position of the first character's match, or `npos` if no
+ // match was found.
+ size_type rfind(string_view s, size_type pos = npos) const
+ noexcept;
+
+ // Overload of `string_view::rfind()` for finding the last given character `c`
+ // within the `string_view`.
+ size_type rfind(char c, size_type pos = npos) const noexcept;
+
+ // string_view::find_first_of()
+ //
+ // Finds the first occurrence of any of the characters in `s` within the
+ // `string_view`, returning the start position of the match, or `npos` if no
+ // match was found.
+ size_type find_first_of(string_view s, size_type pos = 0) const
+ noexcept;
+
+ // Overload of `string_view::find_first_of()` for finding a character `c`
+ // within the `string_view`.
+ size_type find_first_of(char c, size_type pos = 0) const
+ noexcept {
+ return find(c, pos);
+ }
+
+ // string_view::find_last_of()
+ //
+ // Finds the last occurrence of any of the characters in `s` within the
+ // `string_view`, returning the start position of the match, or `npos` if no
+ // match was found.
+ size_type find_last_of(string_view s, size_type pos = npos) const
+ noexcept;
+
+ // Overload of `string_view::find_last_of()` for finding a character `c`
+ // within the `string_view`.
+ size_type find_last_of(char c, size_type pos = npos) const
+ noexcept {
+ return rfind(c, pos);
+ }
+
+ // string_view::find_first_not_of()
+ //
+ // Finds the first occurrence of any of the characters not in `s` within the
+ // `string_view`, returning the start position of the first non-match, or
+ // `npos` if no non-match was found.
+ size_type find_first_not_of(string_view s, size_type pos = 0) const noexcept;
+
+ // Overload of `string_view::find_first_not_of()` for finding a character
+ // that is not `c` within the `string_view`.
+ size_type find_first_not_of(char c, size_type pos = 0) const noexcept;
+
+ // string_view::find_last_not_of()
+ //
+ // Finds the last occurrence of any of the characters not in `s` within the
+ // `string_view`, returning the start position of the last non-match, or
+ // `npos` if no non-match was found.
+ size_type find_last_not_of(string_view s,
+ size_type pos = npos) const noexcept;
+
+ // Overload of `string_view::find_last_not_of()` for finding a character
+ // that is not `c` within the `string_view`.
+ size_type find_last_not_of(char c, size_type pos = npos) const
+ noexcept;
+
+ private:
+ static constexpr size_type kMaxSize =
+ (std::numeric_limits<difference_type>::max)();
+
+ static constexpr size_type CheckLengthInternal(size_type len) {
+ return ABSL_ASSERT(len <= kMaxSize), len;
+ }
+
+ const char* ptr_;
+ size_type length_;
+};
+
+// This large function is defined inline so that in a fairly common case where
+// one of the arguments is a literal, the compiler can elide a lot of the
+// following comparisons.
+inline bool operator==(string_view x, string_view y) noexcept {
+ auto len = x.size();
+ if (len != y.size()) {
+ return false;
+ }
+
+ return x.data() == y.data() || len <= 0 ||
+ 0 == std::memcmp(x.data(), y.data(), len);
+}
+
+inline bool operator!=(string_view x, string_view y) noexcept {
+ return !(x == y);
+}
+
+inline bool operator<(string_view x, string_view y) noexcept {
+ auto min_size = (std::min)(x.size(), y.size());
+ const int r = min_size == 0 ? 0 : memcmp(x.data(), y.data(), min_size);
+ return (r < 0) || (r == 0 && x.size() < y.size());
+}
+
+inline bool operator>(string_view x, string_view y) noexcept { return y < x; }
+
+inline bool operator<=(string_view x, string_view y) noexcept {
+ return !(y < x);
+}
+
+inline bool operator>=(string_view x, string_view y) noexcept {
+ return !(x < y);
+}
+
+// IO Insertion Operator
+std::ostream& operator<<(std::ostream& o, string_view piece);
+
+} // namespace absl
+
+
+
+
+#endif // ABSL_HAVE_STD_STRING_VIEW
+
+namespace absl {
+
+// ClippedSubstr()
+//
+// Like `s.substr(pos, n)`, but clips `pos` to an upper bound of `s.size()`.
+// Provided because std::string_view::substr throws if `pos > size()`
+inline string_view ClippedSubstr(string_view s, size_t pos,
+ size_t n = string_view::npos) {
+ pos = (std::min)(pos, static_cast<size_t>(s.size()));
+ return s.substr(pos, n);
+}
+
+// NullSafeStringView()
+//
+// Creates an `absl::string_view` from a pointer `p` even if it's null-valued.
+// This function should be used where an `absl::string_view` can be created from
+// a possibly-null pointer.
+// Our absl::string_view has historically been constructible from a null-valued
+// pointer, but the same null value isn't valid for std::string_view.
+inline string_view NullSafeStringView(const char* p) {
+ return p ? string_view(p) : string_view();
+}
+
+} // namespace absl
+
+#endif // S2_THIRD_PARTY_ABSL_STRINGS_STRING_VIEW_H_
--- /dev/null
+//
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: strip.h
+// -----------------------------------------------------------------------------
+//
+// This file contains various functions for stripping substrings from a string.
+#ifndef S2_THIRD_PARTY_ABSL_STRINGS_STRIP_H_
+#define S2_THIRD_PARTY_ABSL_STRINGS_STRIP_H_
+
+#include <cstddef>
+#include <string>
+
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/third_party/absl/strings/ascii.h"
+#include "s2/third_party/absl/strings/ascii_ctype.h"
+#include "s2/third_party/absl/strings/match.h"
+#include "s2/third_party/absl/strings/string_view.h"
+
+namespace absl {
+
+// ConsumePrefix()
+//
+// Strips the `expected` prefix from the start of the given string, returning
+// `true` if the strip operation succeeded or false otherwise.
+//
+// Example:
+//
+// absl::string_view input("abc");
+// EXPECT_TRUE(absl::ConsumePrefix(&input, "a"));
+// EXPECT_EQ(input, "bc");
+inline bool ConsumePrefix(absl::string_view* str, absl::string_view expected) {
+ if (!absl::StartsWith(*str, expected)) return false;
+ str->remove_prefix(expected.size());
+ return true;
+}
+// ConsumeSuffix()
+//
+// Strips the `expected` suffix from the end of the given string, returning
+// `true` if the strip operation succeeded or false otherwise.
+//
+// Example:
+//
+// absl::string_view input("abcdef");
+// EXPECT_TRUE(absl::ConsumeSuffix(&input, "def"));
+// EXPECT_EQ(input, "abc");
+inline bool ConsumeSuffix(absl::string_view* str, absl::string_view expected) {
+ if (!absl::EndsWith(*str, expected)) return false;
+ str->remove_suffix(expected.size());
+ return true;
+}
+
+// StripPrefix()
+//
+// Returns a view into the input string 'str' with the given 'prefix' removed,
+// but leaving the original string intact. If the prefix does not match at the
+// start of the string, returns the original string instead.
+ABSL_MUST_USE_RESULT inline absl::string_view StripPrefix(
+ absl::string_view str, absl::string_view prefix) {
+ if (absl::StartsWith(str, prefix)) str.remove_prefix(prefix.size());
+ return str;
+}
+
+// StripSuffix()
+//
+// Returns a view into the input string 'str' with the given 'suffix' removed,
+// but leaving the original string intact. If the suffix does not match at the
+// end of the string, returns the original string instead.
+ABSL_MUST_USE_RESULT inline absl::string_view StripSuffix(
+ absl::string_view str, absl::string_view suffix) {
+ if (absl::EndsWith(str, suffix)) str.remove_suffix(suffix.size());
+ return str;
+}
+
+} // namespace absl
+
+
+// Replaces any of the *bytes* in 'remove' with the *byte* 'replace_with'.
+//
+// *Warning*: This function operates on *bytes* in the remove string.
+// When the remove string contains multi-byte (non-ASCII) characters,
+// then some strings will turn into garbage which will break downstream code.
+// Use icu::UnicodeSet and its spanUTF8()/spanBackUTF8().
+void ReplaceCharacters(char* str, size_t len, absl::string_view remove,
+ char replace_with);
+void ReplaceCharacters(string* s, absl::string_view remove, char replace_with);
+
+// Replaces the character 'remove' with the character 'replace_with'.
+//
+inline void ReplaceCharacter(char* str, size_t len, char remove,
+ char replace_with) {
+ for (char* end = str + len; str != end; ++str) {
+ if (*str == remove) *str = replace_with;
+ }
+}
+
+ABSL_DEPRECATED("Use absl::StripAsciiWhitespace() instead")
+inline void StripWhitespace(string* str) { absl::StripAsciiWhitespace(str); }
+
+ABSL_DEPRECATED("Use absl::StripAsciiWhitespace() instead")
+inline void StripWhitespace(absl::string_view* str) {
+ *str = absl::StripAsciiWhitespace(*str);
+}
+
+// Returns a pointer to the first character in 'str' that is not
+// ASCII whitespace. Never returns nullptr. 'str' must be NUL-terminated.
+inline const char* SkipLeadingWhitespace(const char* str) {
+ while (absl::ascii_isspace(*str)) ++str;
+ return str;
+}
+inline char* SkipLeadingWhitespace(char* str) {
+ while (absl::ascii_isspace(*str)) ++str;
+ return str;
+}
+
+
+#endif // S2_THIRD_PARTY_ABSL_STRINGS_STRIP_H_
--- /dev/null
+//
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// span.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines a `Span<T>` type for holding a view of an existing
+// array of data. The `Span` object, much like the `absl::string_view` object,
+// does not own such data itself. A span provides a lightweight way to pass
+// around view of such data.
+//
+// Additionally, this header file defines `MakeSpan()` and `MakeConstSpan()`
+// factory functions, for clearly creating spans of type `Span<T>` or read-only
+// `Span<const T>` when such types may be difficult to identify due to issues
+// with implicit conversion.
+//
+// The C++ standards committee currently has a proposal for a `std::span` type,
+// (http://wg21.link/p0122), which is not yet part of the standard (though may
+// become part of C++20). As of August 2017, the differences between
+// `absl::Span` and this proposal are:
+// * `absl::Span` uses `size_t` for `size_type`
+// * `absl::Span` has no `operator()`
+// * `absl::Span` has no constructors for `std::unique_ptr` or
+// `std::shared_ptr`
+// * `absl::Span` has the factory functions `MakeSpan()` and
+// `MakeConstSpan()`
+// * `absl::Span` has `front()` and `back()` methods
+// * bounds-checked access to `absl::Span` is accomplished with `at()`
+// * `absl::Span` has compiler-provided move and copy constructors and
+// assignment. This is due to them being specified as `constexpr`, but that
+// implies const in C++11.
+// * `absl::Span` has no `element_type` or `index_type` typedefs
+// * A read-only `absl::Span<const T>` can be implicitly constructed from an
+// initializer list.
+// * `absl::Span` has no `bytes()`, `size_bytes()`, `as_bytes()`, or
+// `as_mutable_bytes()` methods
+// * `absl::Span` has no static extent template parameter, nor constructors
+// which exist only because of the static extent parameter.
+// * `absl::Span` has an explicit mutable-reference constructor
+//
+// For more information, see the class comments below.
+#ifndef S2_THIRD_PARTY_ABSL_TYPES_SPAN_H_
+#define S2_THIRD_PARTY_ABSL_TYPES_SPAN_H_
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <initializer_list>
+#include <iterator>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include "s2/third_party/absl/algorithm/algorithm.h"
+#include "s2/third_party/absl/base/internal/throw_delegate.h"
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/third_party/absl/base/optimization.h"
+#include "s2/third_party/absl/base/port.h"
+#include "s2/third_party/absl/meta/type_traits.h"
+
+namespace absl {
+
+template <typename T>
+class Span;
+
+namespace span_internal {
+// A constexpr min function
+constexpr size_t Min(size_t a, size_t b) noexcept { return a < b ? a : b; }
+
+// Wrappers for access to container data pointers.
+template <typename C>
+constexpr auto GetDataImpl(C& c, char) noexcept // NOLINT(runtime/references)
+ -> decltype(c.data()) {
+ return c.data();
+}
+template <typename C>
+constexpr auto GetDataImpl(C& c, int) noexcept // NOLINT(runtime/references)
+ -> decltype(c.mutable_data()) {
+ return c.mutable_data();
+}
+
+// Before C++17, string::data returns a const char* in all cases.
+inline char* GetDataImpl(string& s, // NOLINT(runtime/references)
+ int) noexcept {
+ return &s[0];
+}
+
+template <typename C>
+constexpr auto GetData(C& c) noexcept // NOLINT(runtime/references)
+ -> decltype(GetDataImpl(c, 0)) {
+ return GetDataImpl(c, 0);
+}
+
+// Detection idioms for size() and data().
+template <typename C>
+using HasSize =
+ std::is_integral<absl::decay_t<decltype(std::declval<C&>().size())>>;
+
+// We want to enable conversion from vector<T*> to Span<const T* const> but
+// disable conversion from vector<Derived> to Span<Base>. Here we use
+// the fact that U** is convertible to Q* const* if and only if Q is the same
+// type or a more cv-qualified version of U. We also decay the result type of
+// data() to avoid problems with classes which have a member function data()
+// which returns a reference.
+template <typename T, typename C>
+using HasData =
+ std::is_convertible<absl::decay_t<decltype(GetData(std::declval<C&>()))>*,
+ T* const*>;
+
+// Extracts value type from a Container
+template <typename C>
+struct ElementType {
+ using type = typename absl::remove_reference_t<C>::value_type;
+};
+
+template <typename T, size_t N>
+struct ElementType<T (&)[N]> {
+ using type = T;
+};
+
+template <typename C>
+using ElementT = typename ElementType<C>::type;
+
+template <typename T>
+using EnableIfMutable =
+ typename std::enable_if<!std::is_const<T>::value, int>::type;
+
+template <typename T>
+bool EqualImpl(Span<T> a, Span<T> b) {
+ static_assert(std::is_const<T>::value, "");
+ return absl::equal(a.begin(), a.end(), b.begin(), b.end());
+}
+
+template <typename T>
+bool LessThanImpl(Span<T> a, Span<T> b) {
+ static_assert(std::is_const<T>::value, "");
+ return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
+}
+
+// The `IsConvertible` classes here are needed because of the
+// `std::is_convertible` bug in libcxx when compiled with GCC. This build
+// configuration is used by Android NDK toolchain. Reference link:
+// https://bugs.llvm.org/show_bug.cgi?id=27538.
+template <typename From, typename To>
+struct IsConvertibleHelper {
+ private:
+ static std::true_type testval(To);
+ static std::false_type testval(...);
+
+ public:
+ using type = decltype(testval(std::declval<From>()));
+};
+
+template <typename From, typename To>
+struct IsConvertible : IsConvertibleHelper<From, To>::type {};
+
+// TODO(user): replace `IsConvertible` with `std::is_convertible` once the
+// older version of libcxx is not supported.
+template <typename From, typename To>
+using EnableIfConvertibleToSpanConst =
+ typename std::enable_if<IsConvertible<From, Span<const To>>::value>::type;
+} // namespace span_internal
+
+//------------------------------------------------------------------------------
+// Span
+//------------------------------------------------------------------------------
+//
+// A `Span` is an "array view" type for holding a view of a contiguous data
+// array; the `Span` object does not and cannot own such data itself. A span
+// provides an easy way to provide overloads for anything operating on
+// contiguous sequences without needing to manage pointers and array lengths
+// manually.
+
+// A span is conceptually a pointer (ptr) and a length (size) into an already
+// existing array of contiguous memory; the array it represents references the
+// elements "ptr[0] .. ptr[size-1]". Passing a properly-constructed `Span`
+// instead of raw pointers avoids many issues related to index out of bounds
+// errors.
+//
+// Spans may also be constructed from containers holding contiguous sequences.
+// Such containers must supply `data()` and `size() const` methods (e.g
+// `std::vector<T>`, `absl::InlinedVector<T, N>`). All implicit conversions to
+// `absl::Span` from such containers will create spans of type `const T`;
+// spans which can mutate their values (of type `T`) must use explicit
+// constructors.
+//
+// A `Span<T>` is somewhat analogous to an `absl::string_view`, but for an array
+// of elements of type `T`. A user of `Span` must ensure that the data being
+// pointed to outlives the `Span` itself.
+//
+// You can construct a `Span<T>` in several ways:
+//
+// * Explicitly from a reference to a container type
+// * Explicitly from a pointer and size
+// * Implicitly from a container type (but only for spans of type `const T`)
+// * Using the `MakeSpan()` or `MakeConstSpan()` factory functions.
+//
+// Examples:
+//
+// // Construct a Span explicitly from a container:
+// std::vector<int> v = {1, 2, 3, 4, 5};
+// auto span = absl::Span<const int>(v);
+//
+// // Construct a Span explicitly from a C-style array:
+// int a[5] = {1, 2, 3, 4, 5};
+// auto span = absl::Span<const int>(a);
+//
+// // Construct a Span implicitly from a container
+// void MyRoutine(absl::Span<const int> a) {
+// ...
+// }
+// std::vector v = {1,2,3,4,5};
+// MyRoutine(v) // convert to Span<const T>
+//
+// Note that `Span` objects, in addition to requiring that the memory they
+// point to remains alive, must also ensure that such memory does not get
+// reallocated. Therefore, to avoid undefined behavior, containers with
+// associated span views should not invoke operations that may reallocate memory
+// (such as resizing) or invalidate iterators into the container.
+//
+// One common use for a `Span` is when passing arguments to a routine that can
+// accept a variety of array types (e.g. a `std::vector`, `absl::InlinedVector`,
+// a C-style array, etc.). Instead of creating overloads for each case, you
+// can simply specify a `Span` as the argument to such a routine.
+//
+// Example:
+//
+// void MyRoutine(absl::Span<const int> a) {
+// ...
+// }
+//
+// std::vector v = {1,2,3,4,5};
+// MyRoutine(v);
+//
+// absl::InlinedVector<int, 4> my_inline_vector;
+// MyRoutine(my_inline_vector);
+//
+// // Explicit constructor from pointer,size
+// int* my_array = new int[10];
+// MyRoutine(absl::Span<const int>(my_array, 10));
+template <typename T>
+class Span {
+ private:
+ // Used to determine whether a Span can be constructed from a container of
+ // type C.
+ template <typename C>
+ using EnableIfConvertibleFrom =
+ typename std::enable_if<span_internal::HasData<T, C>::value &&
+ span_internal::HasSize<C>::value>::type;
+
+ // Used to SFINAE-enable a function when the slice elements are const.
+ template <typename U>
+ using EnableIfConstView =
+ typename std::enable_if<std::is_const<T>::value, U>::type;
+
+ // Used to SFINAE-enable a function when the slice elements are mutable.
+ template <typename U>
+ using EnableIfMutableView =
+ typename std::enable_if<!std::is_const<T>::value, U>::type;
+
+ public:
+ using value_type = absl::remove_cv_t<T>;
+ using pointer = T*;
+ using const_pointer = const T*;
+ using reference = T&;
+ using const_reference = const T&;
+ using iterator = pointer;
+ using const_iterator = const_pointer;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ using size_type = size_t;
+ using difference_type = ptrdiff_t;
+
+ static const size_type npos = ~(size_type(0));
+
+ constexpr Span() noexcept : Span(nullptr, 0) {}
+ constexpr Span(pointer array, size_type length) noexcept
+ : ptr_(array), len_(length) {}
+
+ // Implicit conversion constructors
+ template <size_t N>
+ constexpr Span(T (&a)[N]) noexcept // NOLINT(runtime/explicit)
+ : Span(a, N) {}
+
+ // Substring of another Span.
+ // pos must be non-negative and <= x.length().
+ // len must be non-negative and will be pinned to at most x.length() - pos.
+ // If len==npos, the substring continues till the end of x.
+ ABSL_DEPRECATED("Prefer Span(...).subspan(pos, len)")
+ constexpr Span(Span x, size_type pos, size_type len) noexcept
+ : Span(x.data() + pos, span_internal::Min(x.size() - pos, len)) {}
+
+ // The constructor for any class supplying 'T* data()' or 'T* mutable_data()'
+ // (the former is called if both exist), and 'some_integral_type size()
+ // const'. proto2::RepeatedField is an example of this. Also supports string
+ // arguments, when T==char. The appropriate ctor is selected using SFINAE.
+ template <typename V, typename = EnableIfConvertibleFrom<V>,
+ typename = EnableIfMutableView<V>>
+ ABSL_DEPRECATED("Use the explicit mutable ref conversion ctor or MakeSpan()")
+ Span(V* v) // NOLINT(runtime/explicit)
+ : Span(span_internal::GetData(*v), v->size()) {}
+
+ // Explicit reference constructor for a mutable `Span<T>` type. Can be
+ // replaced with MakeSpan() to infer the type parameter.
+ template <typename V, typename = EnableIfConvertibleFrom<V>,
+ typename = EnableIfMutableView<V>>
+ explicit Span(V& v) noexcept // NOLINT(runtime/references)
+ : Span(span_internal::GetData(v), v.size()) {}
+
+ // Implicit reference constructor for a read-only `Span<const T>` type
+ template <typename V, typename = EnableIfConvertibleFrom<V>,
+ typename = EnableIfConstView<V>>
+ constexpr Span(const V& v) noexcept // NOLINT(runtime/explicit)
+ : Span(span_internal::GetData(v), v.size()) {}
+
+ // Implicit constructor from an initializer list, making it possible to pass a
+ // brace-enclosed initializer list to a function expecting a `Span`. Such
+ // spans constructed from an initializer list must be of type `Span<const T>`.
+ //
+ // void Process(absl::Span<const int> x);
+ // Process({1, 2, 3});
+ //
+ // Note that as always the array referenced by the span must outlive the span.
+ // Since an initializer list constructor acts as if it is fed a temporary
+ // array (cf. C++ standard [dcl.init.list]/5), it's safe to use this
+ // constructor only when the `std::initializer_list` itself outlives the span.
+ // In order to meet this requirement it's sufficient to ensure that neither
+ // the span nor a copy of it is used outside of the expression in which it's
+ // created:
+ //
+ // // Assume that this function uses the array directly, not retaining any
+ // // copy of the span or pointer to any of its elements.
+ // void Process(absl::Span<const int> ints);
+ //
+ // // Okay: the std::initializer_list<int> will reference a temporary array
+ // // that isn't destroyed until after the call to Process returns.
+ // Process({ 17, 19 });
+ //
+ // // Not okay: the storage used by the std::initializer_list<int> is not
+ // // allowed to be referenced after the first line.
+ // absl::Span<const int> ints = { 17, 19 };
+ // Process(ints);
+ //
+ // // Not okay for the same reason as above: even when the elements of the
+ // // initializer list expression are not temporaries the underlying array
+ // // is, so the initializer list must still outlive the span.
+ // const int foo = 17;
+ // absl::Span<const int> ints = { foo };
+ // Process(ints);
+ //
+ template <typename LazyT = T,
+ typename = EnableIfConstView<LazyT>>
+ Span(
+ std::initializer_list<value_type> v) noexcept // NOLINT(runtime/explicit)
+ : Span(v.begin(), v.size()) {}
+
+ // Accessors
+
+ // Span::data()
+ //
+ // Returns a pointer to the span's underlying array of data (which is held
+ // outside the span).
+ constexpr pointer data() const noexcept { return ptr_; }
+
+ // Span::size()
+ //
+ // Returns the size of this span.
+ constexpr size_type size() const noexcept { return len_; }
+
+ // Span::length()
+ //
+ // Returns the length (size) of this span.
+ constexpr size_type length() const noexcept { return size(); }
+
+ // Span::empty()
+ //
+ // Returns a boolean indicating whether or not this span is considered empty.
+ constexpr bool empty() const noexcept { return size() == 0; }
+
+ // Span::operator[]
+ //
+ // Returns a reference to the i'th element of this span.
+ constexpr reference operator[](size_type i) const noexcept {
+ // MSVC 2015 accepts this as constexpr, but not ptr_[i]
+ // TODO(b/65551885) Fix when absl drops MSVC 2015 support
+ return *(data() + i);
+ }
+
+ // Span::at()
+ //
+ // Returns a reference to the i'th element of this span.
+ constexpr reference at(size_type i) const {
+ return ABSL_PREDICT_TRUE(i < size()) //
+ ? *(data() + i)
+ : (base_internal::ThrowStdOutOfRange(
+ "Span::at failed bounds check"),
+ *(data() + i));
+ }
+
+ // Span::front()
+ //
+ // Returns a reference to the first element of this span.
+ constexpr reference front() const noexcept {
+ return ABSL_ASSERT(size() > 0), *data();
+ }
+
+ // Span::back()
+ //
+ // Returns a reference to the last element of this span.
+ constexpr reference back() const noexcept {
+ return ABSL_ASSERT(size() > 0), *(data() + size() - 1);
+ }
+
+ // Span::begin()
+ //
+ // Returns an iterator to the first element of this span.
+ constexpr iterator begin() const noexcept { return data(); }
+
+ // Span::cbegin()
+ //
+ // Returns a const iterator to the first element of this span.
+ constexpr const_iterator cbegin() const noexcept { return begin(); }
+
+ // Span::end()
+ //
+ // Returns an iterator to the last element of this span.
+ constexpr iterator end() const noexcept { return data() + size(); }
+
+ // Span::cend()
+ //
+ // Returns a const iterator to the last element of this span.
+ constexpr const_iterator cend() const noexcept { return end(); }
+
+ // Span::rbegin()
+ //
+ // Returns a reverse iterator starting at the last element of this span.
+ constexpr reverse_iterator rbegin() const noexcept {
+ return reverse_iterator(end());
+ }
+
+ // Span::crbegin()
+ //
+ // Returns a reverse const iterator starting at the last element of this span.
+ constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); }
+
+ // Span::rend()
+ //
+ // Returns a reverse iterator starting at the first element of this span.
+ constexpr reverse_iterator rend() const noexcept {
+ return reverse_iterator(begin());
+ }
+
+ // Span::crend()
+ //
+ // Returns a reverse iterator starting at the first element of this span.
+ constexpr const_reverse_iterator crend() const noexcept { return rend(); }
+
+ // Span mutations
+
+ // Span::remove_prefix()
+ //
+ // Removes the first `n` elements from the span.
+ void remove_prefix(size_type n) noexcept {
+ assert(size() >= n);
+ ptr_ += n;
+ len_ -= n;
+ }
+
+ // Span::remove_suffix()
+ //
+ // Removes the last `n` elements from the span.
+ void remove_suffix(size_type n) noexcept {
+ assert(size() >= n);
+ len_ -= n;
+ }
+
+ // Span::subspan()
+ //
+ // Returns a `Span` starting at element `pos` and of length `len`. Both `pos`
+ // and `len` are of type `size_type` and thus non-negative. Parameter `pos`
+ // must be <= size(). Any `len` value that points past the end of the span
+ // will be trimmed to at most size() - `pos`. A default `len` value of `npos`
+ // ensures the returned subspan continues until the end of the span.
+ //
+ // Examples:
+ //
+ // std::vector<int> vec = {10, 11, 12, 13};
+ // absl::MakeSpan(vec).subspan(1, 2); // {11, 12}
+ // absl::MakeSpan(vec).subspan(2, 8); // {12, 13}
+ // absl::MakeSpan(vec).subspan(1); // {11, 12, 13}
+ // absl::MakeSpan(vec).subspan(4); // {}
+ // absl::MakeSpan(vec).subspan(5); // throws std::out_of_range
+ constexpr Span subspan(size_type pos = 0, size_type len = npos) const {
+ return (pos <= size())
+ ? Span(data() + pos, span_internal::Min(size() - pos, len))
+ : (base_internal::ThrowStdOutOfRange("pos > size()"), Span());
+ }
+
+ // Support for absl::Hash.
+ template <typename H>
+ friend H AbslHashValue(H h, Span v) {
+ return H::combine(H::combine_contiguous(std::move(h), v.data(), v.size()),
+ v.size());
+ }
+
+ private:
+ pointer ptr_;
+ size_type len_;
+};
+
+template <typename T>
+const typename Span<T>::size_type Span<T>::npos;
+
+// Span relationals
+
+// Equality is compared element-by-element, while ordering is lexicographical.
+// We provide three overloads for each operator to cover any combination on the
+// left or right hand side of mutable Span<T>, read-only Span<const T>, and
+// convertible-to-read-only Span<T>.
+// TODO(user): Due to MSVC overload resolution bug with partial ordering
+// template functions, 5 overloads per operator is needed as a workaround. We
+// should update them to 3 overloads per operator using non-deduced context like
+// string_view, i.e.
+// - (Span<T>, Span<T>)
+// - (Span<T>, non_deduced<Span<const T>>)
+// - (non_deduced<Span<const T>>, Span<T>)
+
+// operator==
+template <typename T>
+bool operator==(Span<T> a, Span<T> b) {
+ return span_internal::EqualImpl<const T>(a, b);
+}
+template <typename T>
+bool operator==(Span<const T> a, Span<T> b) {
+ return span_internal::EqualImpl<const T>(a, b);
+}
+template <typename T>
+bool operator==(Span<T> a, Span<const T> b) {
+ return span_internal::EqualImpl<const T>(a, b);
+}
+template <typename T, typename U,
+ typename = span_internal::EnableIfConvertibleToSpanConst<U, T>>
+bool operator==(const U& a, Span<T> b) {
+ return span_internal::EqualImpl<const T>(a, b);
+}
+template <typename T, typename U,
+ typename = span_internal::EnableIfConvertibleToSpanConst<U, T>>
+bool operator==(Span<T> a, const U& b) {
+ return span_internal::EqualImpl<const T>(a, b);
+}
+
+// operator!=
+template <typename T>
+bool operator!=(Span<T> a, Span<T> b) {
+ return !(a == b);
+}
+template <typename T>
+bool operator!=(Span<const T> a, Span<T> b) {
+ return !(a == b);
+}
+template <typename T>
+bool operator!=(Span<T> a, Span<const T> b) {
+ return !(a == b);
+}
+template <typename T, typename U,
+ typename = span_internal::EnableIfConvertibleToSpanConst<U, T>>
+bool operator!=(const U& a, Span<T> b) {
+ return !(a == b);
+}
+template <typename T, typename U,
+ typename = span_internal::EnableIfConvertibleToSpanConst<U, T>>
+bool operator!=(Span<T> a, const U& b) {
+ return !(a == b);
+}
+
+// operator<
+template <typename T>
+bool operator<(Span<T> a, Span<T> b) {
+ return span_internal::LessThanImpl<const T>(a, b);
+}
+template <typename T>
+bool operator<(Span<const T> a, Span<T> b) {
+ return span_internal::LessThanImpl<const T>(a, b);
+}
+template <typename T>
+bool operator<(Span<T> a, Span<const T> b) {
+ return span_internal::LessThanImpl<const T>(a, b);
+}
+template <typename T, typename U,
+ typename = span_internal::EnableIfConvertibleToSpanConst<U, T>>
+bool operator<(const U& a, Span<T> b) {
+ return span_internal::LessThanImpl<const T>(a, b);
+}
+template <typename T, typename U,
+ typename = span_internal::EnableIfConvertibleToSpanConst<U, T>>
+bool operator<(Span<T> a, const U& b) {
+ return span_internal::LessThanImpl<const T>(a, b);
+}
+
+// operator>
+template <typename T>
+bool operator>(Span<T> a, Span<T> b) {
+ return b < a;
+}
+template <typename T>
+bool operator>(Span<const T> a, Span<T> b) {
+ return b < a;
+}
+template <typename T>
+bool operator>(Span<T> a, Span<const T> b) {
+ return b < a;
+}
+template <typename T, typename U,
+ typename = span_internal::EnableIfConvertibleToSpanConst<U, T>>
+bool operator>(const U& a, Span<T> b) {
+ return b < a;
+}
+template <typename T, typename U,
+ typename = span_internal::EnableIfConvertibleToSpanConst<U, T>>
+bool operator>(Span<T> a, const U& b) {
+ return b < a;
+}
+
+// operator<=
+template <typename T>
+bool operator<=(Span<T> a, Span<T> b) {
+ return !(b < a);
+}
+template <typename T>
+bool operator<=(Span<const T> a, Span<T> b) {
+ return !(b < a);
+}
+template <typename T>
+bool operator<=(Span<T> a, Span<const T> b) {
+ return !(b < a);
+}
+template <typename T, typename U,
+ typename = span_internal::EnableIfConvertibleToSpanConst<U, T>>
+bool operator<=(const U& a, Span<T> b) {
+ return !(b < a);
+}
+template <typename T, typename U,
+ typename = span_internal::EnableIfConvertibleToSpanConst<U, T>>
+bool operator<=(Span<T> a, const U& b) {
+ return !(b < a);
+}
+
+// operator>=
+template <typename T>
+bool operator>=(Span<T> a, Span<T> b) {
+ return !(a < b);
+}
+template <typename T>
+bool operator>=(Span<const T> a, Span<T> b) {
+ return !(a < b);
+}
+template <typename T>
+bool operator>=(Span<T> a, Span<const T> b) {
+ return !(a < b);
+}
+template <typename T, typename U,
+ typename = span_internal::EnableIfConvertibleToSpanConst<U, T>>
+bool operator>=(const U& a, Span<T> b) {
+ return !(a < b);
+}
+template <typename T, typename U,
+ typename = span_internal::EnableIfConvertibleToSpanConst<U, T>>
+bool operator>=(Span<T> a, const U& b) {
+ return !(a < b);
+}
+
+// MakeSpan()
+//
+// Constructs a mutable `Span<T>`, deducing `T` automatically from either a
+// container or pointer+size.
+//
+// Because a read-only `Span<const T>` is implicitly constructed from container
+// types regardless of whether the container itself is a const container,
+// constructing mutable spans of type `Span<T>` from containers requires
+// explicit constructors. The container-accepting version of `MakeSpan()`
+// deduces the type of `T` by the constness of the pointer received from the
+// container's `data()` member. Similarly, the pointer-accepting version returns
+// a `Span<const T>` if `T` is `const`, and a `Span<T>` otherwise.
+//
+// Examples:
+//
+// void MyRoutine(absl::Span<MyComplicatedType> a) {
+// ...
+// };
+// // my_vector is a container of non-const types
+// std::vector<MyComplicatedType> my_vector;
+//
+// // Constructing a Span implicitly attempts to create a Span of type
+// // `Span<const T>`
+// MyRoutine(my_vector); // error, type mismatch
+//
+// // Explicitly constructing the Span is verbose
+// MyRoutine(absl::Span<MyComplicatedType>(my_vector));
+//
+// // Use MakeSpan() to make an absl::Span<T>
+// MyRoutine(absl::MakeSpan(my_vector));
+//
+// // Construct a span from an array ptr+size
+// absl::Span<T> my_span() {
+// return absl::MakeSpan(&array[0], num_elements_);
+// }
+//
+//
+// `MakeSpan() differs from `gtl::MakeArraySlice()` in that it automatically
+// deduces the constness of the argument rather than always returning
+// `absl::Span<const T>` as the latter does. If you want the "always const"
+// behavior, use `MakeConstSpan()`.
+//
+template <int&... ExplicitArgumentBarrier, typename T>
+constexpr Span<T> MakeSpan(T* ptr, size_t size) noexcept {
+ return Span<T>(ptr, size);
+}
+
+template <int&... ExplicitArgumentBarrier, typename T>
+Span<T> MakeSpan(T* begin, T* end) noexcept {
+ return ABSL_ASSERT(begin <= end), Span<T>(begin, end - begin);
+}
+
+template <int&... ExplicitArgumentBarrier, typename C>
+constexpr auto MakeSpan(C& c) noexcept // NOLINT(runtime/references)
+ -> decltype(absl::MakeSpan(span_internal::GetData(c), c.size())) {
+ return MakeSpan(span_internal::GetData(c), c.size());
+}
+
+template <int&... ExplicitArgumentBarrier, typename T, size_t N>
+constexpr Span<T> MakeSpan(T (&array)[N]) noexcept {
+ return Span<T>(array, N);
+}
+
+// MakeConstSpan()
+//
+// Constructs a `Span<const T>` as with `MakeSpan`, deducing `T` automatically,
+// but always returning a `Span<const T>`.
+//
+// Examples:
+//
+// void ProcessInts(absl::Span<const int> some_ints);
+//
+// // Call with a pointer and size.
+// int array[3] = { 0, 0, 0 };
+// ProcessInts(absl::MakeConstSpan(&array[0], 3));
+//
+// // Call with a [begin, end) pair.
+// ProcessInts(absl::MakeConstSpan(&array[0], &array[3]));
+//
+// // Call directly with an array.
+// ProcessInts(absl::MakeConstSpan(array));
+//
+// // Call with a contiguous container.
+// std::vector<int> some_ints = ...;
+// ProcessInts(absl::MakeConstSpan(some_ints));
+// ProcessInts(absl::MakeConstSpan(std::vector<int>{ 0, 0, 0 }));
+//
+template <int&... ExplicitArgumentBarrier, typename T>
+constexpr Span<const T> MakeConstSpan(T* ptr, size_t size) noexcept {
+ return Span<const T>(ptr, size);
+}
+
+template <int&... ExplicitArgumentBarrier, typename T>
+Span<const T> MakeConstSpan(T* begin, T* end) noexcept {
+ return ABSL_ASSERT(begin <= end), Span<const T>(begin, end - begin);
+}
+
+template <int&... ExplicitArgumentBarrier, typename C>
+constexpr auto MakeConstSpan(const C& c) noexcept -> decltype(MakeSpan(c)) {
+ return MakeSpan(c);
+}
+
+template <int&... ExplicitArgumentBarrier, typename T, size_t N>
+constexpr Span<const T> MakeConstSpan(const T (&array)[N]) noexcept {
+ return Span<const T>(array, N);
+}
+} // namespace absl
+
+#endif // S2_THIRD_PARTY_ABSL_TYPES_SPAN_H_
--- /dev/null
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// This header file contains C++11 versions of standard <utility> header
+// abstractions available within C++14 and C++17, and are designed to be drop-in
+// replacement for code compliant with C++14 and C++17.
+//
+// The following abstractions are defined:
+//
+// * integer_sequence<T, Ints...> == std::integer_sequence<T, Ints...>
+// * index_sequence<Ints...> == std::index_sequence<Ints...>
+// * make_integer_sequence<T, N> == std::make_integer_sequence<T, N>
+// * make_index_sequence<N> == std::make_index_sequence<N>
+// * index_sequence_for<Ts...> == std::index_sequence_for<Ts...>
+// * apply<Functor, Tuple> == std::apply<Functor, Tuple>
+// * exchange<T> == std::exchange<T>
+//
+// This header file also provides the tag types `in_place_t`, `in_place_type_t`,
+// and `in_place_index_t`, as well as the constant `in_place`, and
+// `constexpr` `std::move()` and `std::forward()` implementations in C++11.
+//
+// References:
+//
+// http://en.cppreference.com/w/cpp/utility/integer_sequence
+// http://en.cppreference.com/w/cpp/utility/apply
+// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3658.html
+//
+
+#ifndef S2_THIRD_PARTY_ABSL_UTILITY_UTILITY_H_
+#define S2_THIRD_PARTY_ABSL_UTILITY_UTILITY_H_
+
+#include <cstddef>
+#include <cstdlib>
+#include <tuple>
+#include <utility>
+
+#include "s2/third_party/absl/base/config.h"
+#include "s2/third_party/absl/base/internal/inline_variable.h"
+#include "s2/third_party/absl/base/internal/invoke.h"
+#include "s2/third_party/absl/meta/type_traits.h"
+
+namespace absl {
+
+// integer_sequence
+//
+// Class template representing a compile-time integer sequence. An instantiation
+// of `integer_sequence<T, Ints...>` has a sequence of integers encoded in its
+// type through its template arguments (which is a common need when
+// working with C++11 variadic templates). `absl::integer_sequence` is designed
+// to be a drop-in replacement for C++14's `std::integer_sequence`.
+//
+// Example:
+//
+// template< class T, T... Ints >
+// void user_function(integer_sequence<T, Ints...>);
+//
+// int main()
+// {
+// // user_function's `T` will be deduced to `int` and `Ints...`
+// // will be deduced to `0, 1, 2, 3, 4`.
+// user_function(make_integer_sequence<int, 5>());
+// }
+template <typename T, T... Ints>
+struct integer_sequence {
+ using value_type = T;
+ static constexpr size_t size() noexcept { return sizeof...(Ints); }
+};
+
+// index_sequence
+//
+// A helper template for an `integer_sequence` of `size_t`,
+// `absl::index_sequence` is designed to be a drop-in replacement for C++14's
+// `std::index_sequence`.
+template <size_t... Ints>
+using index_sequence = integer_sequence<size_t, Ints...>;
+
+namespace utility_internal {
+
+template <typename Seq, size_t SeqSize, size_t Rem>
+struct Extend;
+
+// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency.
+template <typename T, T... Ints, size_t SeqSize>
+struct Extend<integer_sequence<T, Ints...>, SeqSize, 0> {
+ using type = integer_sequence<T, Ints..., (Ints + SeqSize)...>;
+};
+
+template <typename T, T... Ints, size_t SeqSize>
+struct Extend<integer_sequence<T, Ints...>, SeqSize, 1> {
+ using type = integer_sequence<T, Ints..., (Ints + SeqSize)..., 2 * SeqSize>;
+};
+
+// Recursion helper for 'make_integer_sequence<T, N>'.
+// 'Gen<T, N>::type' is an alias for 'integer_sequence<T, 0, 1, ... N-1>'.
+template <typename T, size_t N>
+struct Gen {
+ using type =
+ typename Extend<typename Gen<T, N / 2>::type, N / 2, N % 2>::type;
+};
+
+template <typename T>
+struct Gen<T, 0> {
+ using type = integer_sequence<T>;
+};
+
+} // namespace utility_internal
+
+// Compile-time sequences of integers
+
+// make_integer_sequence
+//
+// This template alias is equivalent to
+// `integer_sequence<int, 0, 1, ..., N-1>`, and is designed to be a drop-in
+// replacement for C++14's `std::make_integer_sequence`.
+template <typename T, T N>
+using make_integer_sequence = typename utility_internal::Gen<T, N>::type;
+
+// make_index_sequence
+//
+// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`,
+// and is designed to be a drop-in replacement for C++14's
+// `std::make_index_sequence`.
+template <size_t N>
+using make_index_sequence = make_integer_sequence<size_t, N>;
+
+// index_sequence_for
+//
+// Converts a typename pack into an index sequence of the same length, and
+// is designed to be a drop-in replacement for C++14's
+// `std::index_sequence_for()`
+template <typename... Ts>
+using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
+
+// Tag types
+
+#ifdef ABSL_HAVE_STD_OPTIONAL
+
+using std::in_place_t;
+using std::in_place;
+
+#else // ABSL_HAVE_STD_OPTIONAL
+
+// in_place_t
+//
+// Tag type used to specify in-place construction, such as with
+// `absl::optional`, designed to be a drop-in replacement for C++17's
+// `std::in_place_t`.
+struct in_place_t {};
+
+ABSL_INTERNAL_INLINE_CONSTEXPR(in_place_t, in_place, {});
+
+#endif // ABSL_HAVE_STD_OPTIONAL
+
+#if defined(ABSL_HAVE_STD_ANY) || defined(ABSL_HAVE_STD_VARIANT)
+using std::in_place_type_t;
+#else
+
+// in_place_type_t
+//
+// Tag type used for in-place construction when the type to construct needs to
+// be specified, such as with `absl::any`, designed to be a drop-in replacement
+// for C++17's `std::in_place_type_t`.
+template <typename T>
+struct in_place_type_t {};
+#endif // ABSL_HAVE_STD_ANY || ABSL_HAVE_STD_VARIANT
+
+#ifdef ABSL_HAVE_STD_VARIANT
+using std::in_place_index_t;
+#else
+
+// in_place_index_t
+//
+// Tag type used for in-place construction when the type to construct needs to
+// be specified, such as with `absl::any`, designed to be a drop-in replacement
+// for C++17's `std::in_place_index_t`.
+template <size_t I>
+struct in_place_index_t {};
+#endif // ABSL_HAVE_STD_VARIANT
+
+// Constexpr move and forward
+
+// move()
+//
+// A constexpr version of `std::move()`, designed to be a drop-in replacement
+// for C++14's `std::move()`.
+template <typename T>
+constexpr absl::remove_reference_t<T>&& move(T&& t) noexcept {
+ return static_cast<absl::remove_reference_t<T>&&>(t);
+}
+
+// forward()
+//
+// A constexpr version of `std::forward()`, designed to be a drop-in replacement
+// for C++14's `std::forward()`.
+template <typename T>
+constexpr T&& forward(
+ absl::remove_reference_t<T>& t) noexcept { // NOLINT(runtime/references)
+ return static_cast<T&&>(t);
+}
+
+namespace utility_internal {
+// Helper method for expanding tuple into a called method.
+template <typename Functor, typename Tuple, std::size_t... Indexes>
+auto apply_helper(Functor&& functor, Tuple&& t, index_sequence<Indexes...>)
+ -> decltype(absl::base_internal::Invoke(
+ absl::forward<Functor>(functor),
+ std::get<Indexes>(absl::forward<Tuple>(t))...)) {
+ return absl::base_internal::Invoke(
+ absl::forward<Functor>(functor),
+ std::get<Indexes>(absl::forward<Tuple>(t))...);
+}
+
+} // namespace utility_internal
+
+// apply
+//
+// Invokes a Callable using elements of a tuple as its arguments.
+// Each element of the tuple corresponds to an argument of the call (in order).
+// Both the Callable argument and the tuple argument are perfect-forwarded.
+// For member-function Callables, the first tuple element acts as the `this`
+// pointer. `absl::apply` is designed to be a drop-in replacement for C++17's
+// `std::apply`. Unlike C++17's `std::apply`, this is not currently `constexpr`.
+//
+// Example:
+//
+// class Foo {
+// public:
+// void Bar(int);
+// };
+// void user_function1(int, string);
+// void user_function2(std::unique_ptr<Foo>);
+// auto user_lambda = [](int, int) {};
+//
+// int main()
+// {
+// std::tuple<int, string> tuple1(42, "bar");
+// // Invokes the first user function on int, string.
+// absl::apply(&user_function1, tuple1);
+//
+// std::tuple<std::unique_ptr<Foo>> tuple2(absl::make_unique<Foo>());
+// // Invokes the user function that takes ownership of the unique
+// // pointer.
+// absl::apply(&user_function2, std::move(tuple2));
+//
+// auto foo = absl::make_unique<Foo>();
+// std::tuple<Foo*, int> tuple3(foo.get(), 42);
+// // Invokes the method Bar on foo with one argument, 42.
+// absl::apply(&Foo::Bar, tuple3);
+//
+// std::tuple<int, int> tuple4(8, 9);
+// // Invokes a lambda.
+// absl::apply(user_lambda, tuple4);
+// }
+template <typename Functor, typename Tuple>
+auto apply(Functor&& functor, Tuple&& t)
+ -> decltype(utility_internal::apply_helper(
+ absl::forward<Functor>(functor), absl::forward<Tuple>(t),
+ absl::make_index_sequence<std::tuple_size<
+ typename std::remove_reference<Tuple>::type>::value>{})) {
+ return utility_internal::apply_helper(
+ absl::forward<Functor>(functor), absl::forward<Tuple>(t),
+ absl::make_index_sequence<std::tuple_size<
+ typename std::remove_reference<Tuple>::type>::value>{});
+}
+
+// exchange
+//
+// Replaces the value of `obj` with `new_value` and returns the old value of
+// `obj`. `absl::exchange` is designed to be a drop-in replacement for C++14's
+// `std::exchange`.
+//
+// Example:
+//
+// Foo& operator=(Foo&& other) {
+// ptr1_ = absl::exchange(other.ptr1_, nullptr);
+// int1_ = absl::exchange(other.int1_, -1);
+// return *this;
+// }
+template <typename T, typename U = T>
+T exchange(T& obj, U&& new_value) {
+ T old_value = absl::move(obj);
+ obj = absl::forward<U>(new_value);
+ return old_value;
+}
+
+} // namespace absl
+
+#endif // S2_THIRD_PARTY_ABSL_UTILITY_UTILITY_H_
--- /dev/null
+// Copyright 2009 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: jyrki@google.com (Jyrki Alakuijala)
+//
+// Interleaving bits quickly by table lookup.
+
+#ifndef S2_UTIL_BITS_BIT_INTERLEAVE_H_
+#define S2_UTIL_BITS_BIT_INTERLEAVE_H_
+
+#include "s2/base/integral_types.h"
+
+namespace util_bits {
+
+// These functions interleave the given arguments into the return value.
+//
+// The 0-bit in val0 will be the 0-bit in the return value.
+// The 0-bit in val1 will be the 1-bit in the return value.
+// The 1-bit of val0 will be the 2-bit in the return value, and so on.
+uint16 InterleaveUint8(uint8 val0, uint8 val1);
+uint32 InterleaveUint16(uint16 val0, uint16 val1);
+uint64 InterleaveUint32(uint32 val0, uint32 val1);
+
+// These functions will decode the interleaved values.
+void DeinterleaveUint8(uint16 code, uint8 *val0, uint8 *val1);
+void DeinterleaveUint16(uint32 code, uint16 *val0, uint16 *val1);
+void DeinterleaveUint32(uint64 code, uint32 *val0, uint32 *val1);
+
+// These functions interleave three arguments into the return value.
+// The 0-bit in val0 will be the 0-bit in the return value.
+// The 0-bit in val1 will be the 1-bit in the return value.
+// The 0-bit in val2 will be the 2-bit in the return value.
+// The 1-bit of val0 will be the 3-bit in the return value, and so on.
+uint32 InterleaveUint8(uint8 val0, uint8 val1, uint8 val2);
+
+// These functions will decode the interleaved values.
+void DeinterleaveUint8(uint32 code, uint8 *val0, uint8* val1, uint8* val2);
+
+} // namespace util_bits
+
+#endif // S2_UTIL_BITS_BIT_INTERLEAVE_H_
--- /dev/null
+// Copyright 2002 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef S2_UTIL_BITS_BITS_H_
+#define S2_UTIL_BITS_BITS_H_
+
+//
+// Various bit-twiddling functions, all of which are static members of the Bits
+// class (making it effectively a namespace). Operands are unsigned integers.
+// Munging bits in _signed_ integers is fraught with peril! For example,
+// -5 << n has undefined behavior (for some values of n).
+//
+// Bits provide the following:
+//
+// * Count(Ones.*|LeadingZeros.*)? . In a similar vein, there's also the
+// Find[LM]SBSetNonZero.* family of functions. You can think of them as
+// (trailing|leading) zero bit count + 1. Also in a similar vein,
+// (Capped)?Difference, which count the number of one bits in foo ^ bar.
+//
+// * ReverseBits${power_of_two}
+//
+// * Log2(Floor|Ceiling)(NonZero)?.* - The NonZero variants have undefined
+// behavior if argument is 0.
+//
+// * Bytes(ContainByte(LessThan)?|AllInRange) - These scan a sequence of bytes
+// looking for one with(out)? some property.
+//
+// * (Get|Set|Copy)Bits
+//
+// * GetLowBits - Extract N lowest bits from value.
+//
+// The only other thing is BitPattern, which is a trait class template (not in
+// Bits) containing a few bit patterns (which vary based on value of template
+// parameter).
+
+#include "s2/third_party/absl/base/casts.h"
+#include "s2/third_party/absl/numeric/int128.h"
+#if defined(__i386__) || defined(__x86_64__)
+#include <x86intrin.h>
+#endif
+
+#include <type_traits>
+
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+#include "s2/base/port.h"
+#include "s2/third_party/absl/base/macros.h"
+
+class Bits {
+ public:
+ // A traits class template for unsigned integer type sizes. Primary
+ // information contained herein is corresponding (unsigned) integer type.
+ // E.g. UnsignedTypeBySize<32>::Type is uint32. Used by UnsignedType.
+ template<int size /* in bytes */>
+ struct UnsignedTypeBySize;
+
+ // Auxiliary struct for figuring out an unsigned type for a given type.
+ template<typename T> struct UnsignedType {
+ typedef typename UnsignedTypeBySize<sizeof(T)>::Type Type;
+ };
+
+ // Return the number of one bits in the given integer.
+ static int CountOnesInByte(unsigned char n);
+
+ static int CountOnes(uint32 n) {
+#if defined(__powerpc64__) && defined(__GNUC__)
+ // Use popcount builtin if we know it is inlined and fast.
+ return PopcountWithBuiltin(n);
+#elif (defined(__i386__) || defined(__x86_64__)) && defined(__POPCNT__) && \
+ defined(__GNUC__)
+ return PopcountWithBuiltin(n);
+#else
+ n -= ((n >> 1) & 0x55555555);
+ n = ((n >> 2) & 0x33333333) + (n & 0x33333333);
+ return static_cast<int>((((n + (n >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24);
+#endif
+ }
+
+ // Count bits using sideways addition [WWG'57]. See Knuth TAOCP v4 7.1.3(59)
+ static inline int CountOnes64(uint64 n) {
+#if defined(__powerpc64__) && defined(__GNUC__)
+ return PopcountWithBuiltin(n);
+#elif defined(__x86_64__) && defined(__POPCNT__) && defined(__GNUC__)
+ return PopcountWithBuiltin(n);
+#elif defined(_LP64)
+ n -= (n >> 1) & 0x5555555555555555ULL;
+ n = ((n >> 2) & 0x3333333333333333ULL) + (n & 0x3333333333333333ULL);
+ return static_cast<int>(
+ (((n + (n >> 4)) & 0xF0F0F0F0F0F0F0FULL) * 0x101010101010101ULL) >> 56);
+#else
+ return CountOnes(n >> 32) + CountOnes(n & 0xffffffff);
+#endif
+ }
+
+ // Count bits in uint128
+ static inline int CountOnes128(absl::uint128 n) {
+ return Bits::CountOnes64(absl::Uint128High64(n)) +
+ Bits::CountOnes64(absl::Uint128Low64(n));
+ }
+
+ // Count leading zeroes. This is similar to wordsize - 1 - floor(log2(n)).
+ // Returns number of bits if n is 0.
+ static inline int CountLeadingZeros32(uint32 n) {
+ // Instead of using __builtin_clz(), we explicitly write target specific
+ // assembly because we want to handle n == 0. If we used __builtin_clz(),
+ // we would need to use something like "n ? __builtin_clz(n) : 32". The
+ // check is not necessary on POWER and aarch64 but we cannot depend on
+ // that because __builtin_clz(0) is documented to be undefined.
+#if defined(__aarch64__) && defined(__GNUC__)
+ int32 count;
+ asm("clz %w0,%w1" : "=r"(count) : "r"(n));
+ return count;
+#elif (defined(__i386__) || defined(__x86_64__)) && defined(__LZCNT__) && \
+ defined(__GNUC__)
+ return __lzcnt32(n);
+#elif (defined(__i386__) || defined(__x86_64__)) && defined(__GNUC__)
+ if (n == 0) return 32;
+ int32 idx;
+ asm("bsr %1, %0"
+ : "=r"(idx)
+ : "ro"(n)
+ : "cc"); // bsr writes Z flag
+ return 31 ^ idx;
+#elif defined(__powerpc64__) && defined(__GNUC__)
+ int32 count;
+ asm("cntlzw %0,%1" : "=r"(count) : "r"(n));
+ return count;
+#elif defined(__GNUC__)
+ return CountLeadingZerosWithBuiltin(n);
+#else
+ return CountLeadingZeros32_Portable(n);
+#endif
+ }
+
+ static inline int CountLeadingZeros64(uint64 n) {
+#if defined(__aarch64__) && defined(__GNUC__)
+ int64 count;
+ asm("clz %0,%1" : "=r"(count) : "r"(n));
+ return static_cast<int>(count);
+#elif defined(__powerpc64__) && defined(__GNUC__)
+ int64 count;
+ asm("cntlzd %0,%1" : "=r"(count) : "r"(n));
+ return static_cast<int>(count);
+#elif (defined(__i386__) || defined(__x86_64__)) && defined(__LZCNT__) && \
+ defined(__GNUC__)
+ return __lzcnt64(n);
+#elif defined(__x86_64__) && defined(__GNUC__)
+ if (n == 0) return 64;
+ int64 idx;
+ asm ("bsr %1, %0"
+ : "=r"(idx)
+ : "ro"(n)
+ : "cc"); // bsr writes Z flag
+ return static_cast<int>(63 ^ idx);
+#elif defined(__GNUC__)
+ return CountLeadingZerosWithBuiltin(n);
+#else
+ return CountLeadingZeros64_Portable(n);
+#endif
+ }
+
+ static inline int CountLeadingZeros128(absl::uint128 n) {
+ if (uint64 hi = absl::Uint128High64(n))
+ return Bits::CountLeadingZeros64(hi);
+ return Bits::CountLeadingZeros64(absl::Uint128Low64(n)) + 64;
+ }
+
+ // Reverse the bits in the given integer.
+ static uint8 ReverseBits8(uint8 n);
+ static uint32 ReverseBits32(uint32 n);
+ static uint64 ReverseBits64(uint64 n);
+ static absl::uint128 ReverseBits128(absl::uint128 n);
+
+ // Return the number of one bits in the byte sequence.
+ static int Count(const void *m, int num_bytes);
+
+ // Return the number of different bits in the given byte sequences.
+ // (i.e., the Hamming distance)
+ static int Difference(const void *m1, const void *m2, int num_bytes);
+
+ // Return the number of different bits in the given byte sequences,
+ // up to a maximum. Values larger than the maximum may be returned
+ // (because multiple bits are checked at a time), but the function
+ // may exit early if the cap is exceeded.
+ static int CappedDifference(const void *m1, const void *m2,
+ int num_bytes, int cap);
+
+ // Return floor(log2(n)) for positive integer n. Returns -1 iff n == 0.
+ static int Log2Floor(uint32 n);
+ static int Log2Floor64(uint64 n);
+ static int Log2Floor128(absl::uint128 n);
+
+ // Potentially faster version of Log2Floor() that returns an
+ // undefined value if n == 0
+ static int Log2FloorNonZero(uint32 n);
+ static int Log2FloorNonZero64(uint64 n);
+ static int Log2FloorNonZero128(absl::uint128 n);
+
+ // Return ceiling(log2(n)) for positive integer n. Returns -1 iff n == 0.
+ static int Log2Ceiling(uint32 n);
+ static int Log2Ceiling64(uint64 n);
+ static int Log2Ceiling128(absl::uint128 n);
+
+ // Return the first set least / most significant bit, 0-indexed. Returns an
+ // undefined value if n == 0. FindLSBSetNonZero() is similar to ffs() except
+ // that it's 0-indexed, while FindMSBSetNonZero() is the same as
+ // Log2FloorNonZero().
+ static int FindLSBSetNonZero(uint32 n);
+ static int FindLSBSetNonZero64(uint64 n);
+ static int FindLSBSetNonZero128(absl::uint128 n);
+ static int FindMSBSetNonZero(uint32 n) { return Log2FloorNonZero(n); }
+ static int FindMSBSetNonZero64(uint64 n) { return Log2FloorNonZero64(n); }
+ static int FindMSBSetNonZero128(absl::uint128 n) {
+ return Log2FloorNonZero128(n);
+ }
+
+ // Viewing bytes as a stream of unsigned bytes, does that stream
+ // contain any byte equal to c?
+ template <class T> static bool BytesContainByte(T bytes, uint8 c);
+
+ // Viewing bytes as a stream of unsigned bytes, does that stream
+ // contain any byte b < c?
+ template <class T> static bool BytesContainByteLessThan(T bytes, uint8 c);
+
+ // Viewing bytes as a stream of unsigned bytes, are all elements of that
+ // stream in [lo, hi]?
+ template <class T> static bool BytesAllInRange(T bytes, uint8 lo, uint8 hi);
+
+ // Extract 'nbits' consecutive bits from 'src'. Position of bits are
+ // specified by 'offset' from the LSB. 'T' is a scalar type (integral,
+ // float or pointer) whose size is the same as one of the unsigned types.
+ // The return type is an unsigned type having the same size as T.
+ template<typename T>
+ static typename UnsignedType<T>::Type GetBits(const T src,
+ const int offset,
+ const int nbits) {
+ typedef typename UnsignedType<T>::Type UnsignedT;
+ const UnsignedT unsigned_src = absl::bit_cast<UnsignedT>(src);
+ S2_DCHECK_GT(sizeof(UnsignedT) * 8, offset);
+ S2_DCHECK_GE(sizeof(UnsignedT) * 8, offset + nbits);
+ return GetBitsImpl(unsigned_src, offset, nbits);
+ }
+
+ // Overwrite 'nbits' consecutive bits of 'dest.'. Position of bits are
+ // specified by an offset from the LSB. 'T' is a scalar type (integral,
+ // float or pointer) whose size is the same as one of the unsigned types.
+ template<typename T>
+ static void SetBits(const typename UnsignedType<T>::Type value,
+ const int offset,
+ const int nbits,
+ T* const dest) {
+ typedef typename UnsignedType<T>::Type UnsignedT;
+ const UnsignedT unsigned_dest = absl::bit_cast<UnsignedT>(*dest);
+ S2_DCHECK_GT(sizeof(UnsignedT) * 8, offset);
+ S2_DCHECK_GE(sizeof(UnsignedT) * 8, offset + nbits);
+ const UnsignedT mask = NBitsFromLSB<UnsignedT>(nbits);
+ const UnsignedT unsigned_result =
+ (unsigned_dest & ~(mask << offset)) | ((value & mask) << offset);
+ *dest = absl::bit_cast<T>(unsigned_result);
+ }
+
+ // Combine SetBits and GetBits for convenience. This is meant to be a
+ // replacement for BitCopy() for some use cases. Unlike BitCopy(),
+ // Bits::CopyBits() operating on multibyte types has the same behavior on
+ // big-endian and little-endian machines. Sample usage:
+ //
+ // uint32 a, b;
+ // Bits::CopyBits(&a, 0, b, 12, 3);
+ template<typename DestType, typename SrcType>
+ static void CopyBits(DestType* const dest,
+ const int dest_offset,
+ const SrcType src,
+ const int src_offset,
+ const int nbits) {
+ const typename UnsignedType<SrcType>::Type value =
+ GetBits(src, src_offset, nbits);
+ SetBits(value, dest_offset, nbits, dest);
+ }
+
+ // Extract the lowest 'nbits' consecutive bits from 'src'.
+ // Bits::GetLowBits(13, 3); /* = 5 (0b1101 => 0b101) */
+ template<typename T>
+ static typename UnsignedType<T>::Type GetLowBits(const T src,
+ const int nbits) {
+ typedef typename UnsignedType<T>::Type UnsignedT;
+ const UnsignedT unsigned_src = absl::bit_cast<UnsignedT>(src);
+ S2_DCHECK_GE(sizeof(UnsignedT) * 8, nbits);
+ return GetLowBitsImpl(unsigned_src, nbits);
+ }
+
+ private:
+ // We only use this for unsigned types and for 0 <= n <= sizeof(UnsignedT).
+ template<typename UnsignedT>
+ static UnsignedT NBitsFromLSB(const int nbits) {
+ const UnsignedT all_ones = ~static_cast<UnsignedT>(0);
+ return nbits == 0 ? static_cast<UnsignedT>(0)
+ : all_ones >> (sizeof(UnsignedT) * 8 - nbits);
+ }
+
+ template<typename UnsignedT>
+ static inline UnsignedT GetBitsImpl(const UnsignedT src,
+ const int offset,
+ const int nbits);
+ template <typename UnsignedT>
+ static inline UnsignedT GetLowBitsImpl(const UnsignedT src, const int nbits);
+
+#ifdef __GNUC__
+ static int CountLeadingZerosWithBuiltin(unsigned n);
+ // NOLINTNEXTLINE(runtime/int)
+ static int CountLeadingZerosWithBuiltin(unsigned long n);
+ // NOLINTNEXTLINE(runtime/int)
+ static int CountLeadingZerosWithBuiltin(unsigned long long n);
+ static int PopcountWithBuiltin(unsigned n);
+ static int PopcountWithBuiltin(unsigned long n); // NOLINT(runtime/int)
+ static int PopcountWithBuiltin(unsigned long long n); // NOLINT(runtime/int)
+#if defined(__BMI__) && (defined(__i386__) || defined(__x86_64__))
+ static inline uint32 GetBitsImpl(const uint32 src,
+ const int offset,
+ const int nbits);
+#endif
+#if defined(__BMI__) && defined(__x86_64__)
+ static inline uint64 GetBitsImpl(const uint64 src,
+ const int offset,
+ const int nbits);
+#endif
+#if defined(__BMI2__) && (defined(__i386__) || defined(__x86_64__))
+ static inline uint32 GetLowBitsImpl(const uint32 src, const int nbits);
+#endif
+#if defined(__BMI2__) && defined(__x86_64__)
+ static inline uint64 GetLowBitsImpl(const uint64 src, const int nbits);
+#endif
+#endif // __GNUC__
+
+ // Portable implementations.
+ static int Log2Floor_Portable(uint32 n);
+ static int Log2Floor64_Portable(uint64 n);
+ static int Log2FloorNonZero_Portable(uint32 n);
+ static int Log2FloorNonZero64_Portable(uint64 n);
+ static int CountLeadingZeros32_Portable(uint32 n);
+ static int CountLeadingZeros64_Portable(uint64 n);
+ static int FindLSBSetNonZero_Portable(uint32 n);
+ static int FindLSBSetNonZero64_Portable(uint64 n);
+
+ static const char num_bits[];
+ Bits(Bits const&) = delete;
+ void operator=(Bits const&) = delete;
+};
+
+// A utility class for some handy bit patterns. The names l and h
+// were chosen to match Knuth Volume 4: l is 0x010101... and h is 0x808080...;
+// half_ones is ones in the lower half only. We assume sizeof(T) is 1 or even.
+template <class T> struct BitPattern {
+ typedef typename std::make_unsigned<T>::type U;
+ static const U half_ones = (static_cast<U>(1) << (sizeof(U) * 4)) - 1;
+ static const U l =
+ (sizeof(U) == 1) ? 1 : (half_ones / 0xff * (half_ones + 2));
+ static const U h = ~(l * 0x7f);
+};
+
+// ------------------------------------------------------------------------
+// Implementation details follow
+// ------------------------------------------------------------------------
+
+#if defined(__GNUC__)
+
+inline int Bits::Log2Floor(uint32 n) {
+ return n == 0 ? -1 : 31 ^ __builtin_clz(n);
+}
+
+inline int Bits::Log2FloorNonZero(uint32 n) {
+ return 31 ^ __builtin_clz(n);
+}
+
+inline int Bits::FindLSBSetNonZero(uint32 n) {
+ return __builtin_ctz(n);
+}
+
+inline int Bits::Log2Floor64(uint64 n) {
+ return n == 0 ? -1 : 63 ^ __builtin_clzll(n);
+}
+
+inline int Bits::Log2FloorNonZero64(uint64 n) {
+ return 63 ^ __builtin_clzll(n);
+}
+
+inline int Bits::FindLSBSetNonZero64(uint64 n) {
+ return __builtin_ctzll(n);
+}
+
+#elif defined(_MSC_VER)
+
+inline int Bits::FindLSBSetNonZero(uint32 n) {
+ return Bits::FindLSBSetNonZero_Portable(n);
+}
+
+inline int Bits::FindLSBSetNonZero64(uint64 n) {
+ return Bits::FindLSBSetNonZero64_Portable(n);
+}
+
+inline int Bits::Log2FloorNonZero(uint32 n) {
+#ifdef _M_IX86
+ _asm {
+ bsr ebx, n
+ mov n, ebx
+ }
+ return n;
+#else
+ return Bits::Log2FloorNonZero_Portable(n);
+#endif
+}
+
+inline int Bits::Log2Floor(uint32 n) {
+#ifdef _M_IX86
+ _asm {
+ xor ebx, ebx
+ mov eax, n
+ and eax, eax
+ jz return_ebx
+ bsr ebx, eax
+return_ebx:
+ mov n, ebx
+ }
+ return n;
+#else
+ return Bits::Log2Floor_Portable(n);
+#endif
+}
+
+inline int Bits::Log2Floor64(uint64 n) {
+ return Bits::Log2Floor64_Portable(n);
+}
+
+inline int Bits::Log2FloorNonZero64(uint64 n) {
+ return Bits::Log2FloorNonZero64_Portable(n);
+}
+
+#else // !__GNUC__ && !_MSC_VER
+
+inline int Bits::Log2Floor(uint32 n) {
+ return Bits::Log2Floor_Portable(n);
+}
+
+inline int Bits::Log2FloorNonZero(uint32 n) {
+ return Bits::Log2FloorNonZero_Portable(n);
+}
+
+inline int Bits::FindLSBSetNonZero(uint32 n) {
+ return Bits::FindLSBSetNonZero_Portable(n);
+}
+
+inline int Bits::Log2Floor64(uint64 n) {
+ return Bits::Log2Floor64_Portable(n);
+}
+
+inline int Bits::Log2FloorNonZero64(uint64 n) {
+ return Bits::Log2FloorNonZero64_Portable(n);
+}
+
+inline int Bits::FindLSBSetNonZero64(uint64 n) {
+ return Bits::FindLSBSetNonZero64_Portable(n);
+}
+
+#endif
+
+inline int Bits::Log2Floor128(absl::uint128 n) {
+ if (uint64 hi = absl::Uint128High64(n)) return 64 + Log2FloorNonZero64(hi);
+ return Log2Floor64(absl::Uint128Low64(n));
+}
+
+inline int Bits::Log2FloorNonZero128(absl::uint128 n) {
+ if (uint64 hi = absl::Uint128High64(n)) return 64 + Log2FloorNonZero64(hi);
+ return Log2FloorNonZero64(absl::Uint128Low64(n));
+}
+
+inline int Bits::FindLSBSetNonZero128(absl::uint128 n) {
+ if (uint64 lo = absl::Uint128Low64(n)) return Bits::FindLSBSetNonZero64(lo);
+ return 64 + Bits::FindLSBSetNonZero64(absl::Uint128High64(n));
+}
+
+inline int Bits::CountOnesInByte(unsigned char n) {
+ return num_bits[n];
+}
+
+inline uint8 Bits::ReverseBits8(unsigned char n) {
+#if defined(__aarch64__) && defined(__GNUC__)
+ // aarch64 has a reverse bits instruction but there is no gcc builtin.
+ uint32 result;
+ const uint32 n_shifted = static_cast<uint32>(n) << 24;
+ asm("rbit %w0, %w1" : "=r"(result) : "r"(n_shifted));
+ return static_cast<uint8>(result);
+#elif defined (__powerpc64__)
+ uint64 temp = n;
+ // bpermd selects a byte's worth of bits from its second input. Grab one byte
+ // at a time, in reversed order. 0x3f is the lowest order bit of a 64-bit int.
+ // Bits 0x0 through 0x37 will all be zero, and bits 0x38 through 0x3f will
+ // hold the 8 bits from `n`.
+ uint64 result = __builtin_bpermd(0x3f3e3d3c3b3a3938, temp);
+ return static_cast<unsigned char>(result);
+#else
+ n = static_cast<unsigned char>(((n >> 1) & 0x55) | ((n & 0x55) << 1));
+ n = static_cast<unsigned char>(((n >> 2) & 0x33) | ((n & 0x33) << 2));
+ return static_cast<unsigned char>(((n >> 4) & 0x0f) | ((n & 0x0f) << 4));
+#endif
+}
+
+inline uint32 Bits::ReverseBits32(uint32 n) {
+#if defined(__aarch64__) && defined(__GNUC__)
+ uint32 result;
+ asm("rbit %w0, %w1" : "=r"(result) : "r"(n));
+ return result;
+#elif defined(__powerpc64__)
+ uint64 temp = n;
+ uint64 result_0 = __builtin_bpermd(0x3f3e3d3c3b3a3938, temp) << 24;
+ uint64 result_1 = __builtin_bpermd(0x3736353433323130, temp) << 16;
+ uint64 result_2 = __builtin_bpermd(0x2f2e2d2c2b2a2928, temp) << 8;
+ uint64 result_3 = __builtin_bpermd(0x2726252423222120, temp);
+ return static_cast<uint32>(result_0 | result_1 | result_2 | result_3);
+#else
+ n = ((n >> 1) & 0x55555555) | ((n & 0x55555555) << 1);
+ n = ((n >> 2) & 0x33333333) | ((n & 0x33333333) << 2);
+ n = ((n >> 4) & 0x0F0F0F0F) | ((n & 0x0F0F0F0F) << 4);
+ return bswap_32(n);
+#endif
+}
+
+inline uint64 Bits::ReverseBits64(uint64 n) {
+#if defined(__aarch64__) && defined(__GNUC__)
+ uint64 result;
+ asm("rbit %0, %1" : "=r"(result) : "r"(n));
+ return result;
+#elif defined(__powerpc64__)
+ uint64 result_lo0 = __builtin_bpermd(0x3f3e3d3c3b3a3938, n) << 56;
+ uint64 result_lo1 = __builtin_bpermd(0x3736353433323130, n) << 48;
+ uint64 result_lo2 = __builtin_bpermd(0x2f2e2d2c2b2a2928, n) << 40;
+ uint64 result_lo3 = __builtin_bpermd(0x2726252423222120, n) << 32;
+ uint64 result_hi0 = __builtin_bpermd(0x1f1e1d1c1b1a1918, n) << 24;
+ uint64 result_hi1 = __builtin_bpermd(0x1716151413121110, n) << 16;
+ uint64 result_hi2 = __builtin_bpermd(0x0f0e0d0c0b0a0908, n) << 8;
+ uint64 result_hi3 = __builtin_bpermd(0x0706050403020100, n);
+ return (result_lo0 | result_lo1 | result_lo2 | result_lo3 |
+ result_hi0 | result_hi1 | result_hi2 | result_hi3);
+#elif defined(_LP64)
+ n = ((n >> 1) & 0x5555555555555555ULL) | ((n & 0x5555555555555555ULL) << 1);
+ n = ((n >> 2) & 0x3333333333333333ULL) | ((n & 0x3333333333333333ULL) << 2);
+ n = ((n >> 4) & 0x0F0F0F0F0F0F0F0FULL) | ((n & 0x0F0F0F0F0F0F0F0FULL) << 4);
+ return bswap_64(n);
+#else
+ return ReverseBits32( n >> 32 ) |
+ (static_cast<uint64>(ReverseBits32(n & 0xffffffff)) << 32);
+#endif
+}
+
+inline absl::uint128 Bits::ReverseBits128(absl::uint128 n) {
+ return absl::MakeUint128(ReverseBits64(absl::Uint128Low64(n)),
+ ReverseBits64(absl::Uint128High64(n)));
+}
+
+inline int Bits::Log2FloorNonZero_Portable(uint32 n) {
+ // Just use the common routine
+ return Log2Floor(n);
+}
+
+// Log2Floor64() is defined in terms of Log2Floor32(), Log2FloorNonZero32()
+inline int Bits::Log2Floor64_Portable(uint64 n) {
+ const uint32 topbits = static_cast<uint32>(n >> 32);
+ if (topbits == 0) {
+ // Top bits are zero, so scan in bottom bits
+ return Log2Floor(static_cast<uint32>(n));
+ } else {
+ return 32 + Log2FloorNonZero(topbits);
+ }
+}
+
+// Log2FloorNonZero64() is defined in terms of Log2FloorNonZero32()
+inline int Bits::Log2FloorNonZero64_Portable(uint64 n) {
+ const uint32 topbits = static_cast<uint32>(n >> 32);
+ if (topbits == 0) {
+ // Top bits are zero, so scan in bottom bits
+ return Log2FloorNonZero(static_cast<uint32>(n));
+ } else {
+ return 32 + Log2FloorNonZero(topbits);
+ }
+}
+
+// FindLSBSetNonZero64() is defined in terms of FindLSBSetNonZero()
+inline int Bits::FindLSBSetNonZero64_Portable(uint64 n) {
+ const uint32 bottombits = static_cast<uint32>(n);
+ if (bottombits == 0) {
+ // Bottom bits are zero, so scan in top bits
+ return 32 + FindLSBSetNonZero(static_cast<uint32>(n >> 32));
+ } else {
+ return FindLSBSetNonZero(bottombits);
+ }
+}
+
+template <class T>
+inline bool Bits::BytesContainByteLessThan(T bytes, uint8 c) {
+ auto l = BitPattern<T>::l;
+ auto h = BitPattern<T>::h;
+ // The c <= 0x80 code is straight out of Knuth Volume 4.
+ // Usually c will be manifestly constant.
+ return c <= 0x80 ?
+ ((h & (bytes - l * c) & ~bytes) != 0) :
+ ((((bytes - l * c) | (bytes ^ h)) & h) != 0);
+}
+
+template <class T> inline bool Bits::BytesContainByte(T bytes, uint8 c) {
+ // Usually c will be manifestly constant.
+ return Bits::BytesContainByteLessThan<T>(bytes ^ (c * BitPattern<T>::l), 1);
+}
+
+template <class T>
+inline bool Bits::BytesAllInRange(T bytes, uint8 lo, uint8 hi) {
+ auto l = BitPattern<T>::l;
+ auto h = BitPattern<T>::h;
+ // In the common case, lo and hi are manifest constants.
+ if (lo > hi) {
+ return false;
+ }
+ if (hi - lo < 128) {
+ auto x = bytes - l * lo;
+ auto y = bytes + l * (127 - hi);
+ return ((x | y) & h) == 0;
+ }
+ return !Bits::BytesContainByteLessThan(bytes + (255 - hi) * l,
+ lo + (255 - hi));
+}
+
+// Specializations for Bits::UnsignedTypeBySize. For unsupported type
+// sizes, a compile-time error will be generated.
+template<>
+struct Bits::UnsignedTypeBySize<1> {
+ typedef uint8 Type;
+};
+
+template<>
+struct Bits::UnsignedTypeBySize<2> {
+ typedef uint16 Type;
+};
+
+template<>
+struct Bits::UnsignedTypeBySize<4> {
+ typedef uint32 Type;
+};
+
+template<>
+struct Bits::UnsignedTypeBySize<8> {
+ typedef uint64 Type;
+};
+
+template<>
+struct Bits::UnsignedTypeBySize<16> {
+ typedef absl::uint128 Type;
+};
+
+#ifdef __GNUC__
+inline int Bits::CountLeadingZerosWithBuiltin(unsigned n) {
+ if (n == 0) {
+ return sizeof(n) * 8; // __builtin_clz(0) is undefined.
+ }
+ return __builtin_clz(n);
+}
+// NOLINTNEXTLINE(runtime/int)
+inline int Bits::CountLeadingZerosWithBuiltin(unsigned long n) {
+ if (n == 0) {
+ return sizeof(n) * 8; // __builtin_clzl(0) is undefined.
+ }
+ return __builtin_clzl(n);
+}
+// NOLINTNEXTLINE(runtime/int)
+inline int Bits::CountLeadingZerosWithBuiltin(unsigned long long n) {
+ if (n == 0) {
+ return sizeof(n) * 8; // __builtin_clzll(0) is undefined.
+ }
+ return __builtin_clzll(n);
+}
+
+inline int Bits::PopcountWithBuiltin(unsigned n) {
+ return __builtin_popcount(n);
+}
+// NOLINTNEXTLINE(runtime/int)
+inline int Bits::PopcountWithBuiltin(unsigned long n) {
+ return __builtin_popcountl(n);
+}
+// NOLINTNEXTLINE(runtime/int)
+inline int Bits::PopcountWithBuiltin(unsigned long long n) {
+ return __builtin_popcountll(n);
+}
+
+#if defined(__BMI__) && (defined(__i386__) || defined(__x86_64__))
+inline uint32 Bits::GetBitsImpl(const uint32 src,
+ const int offset,
+ const int nbits) {
+ return _bextr_u32(src, offset, nbits);
+}
+#endif
+
+#if defined(__BMI__) && defined(__x86_64__)
+inline uint64 Bits::GetBitsImpl(const uint64 src,
+ const int offset,
+ const int nbits) {
+ return _bextr_u64(src, offset, nbits);
+}
+#endif
+
+#if defined(__BMI2__) && (defined(__i386__) || defined(__x86_64__))
+inline uint32 Bits::GetLowBitsImpl(const uint32 src, const int nbits) {
+ return _bzhi_u32(src, nbits);
+}
+#endif
+
+#if defined(__BMI2__) && defined(__x86_64__)
+inline uint64 Bits::GetLowBitsImpl(const uint64 src, const int nbits) {
+ return _bzhi_u64(src, nbits);
+}
+#endif
+
+#endif // __GNUC__
+
+template<typename UnsignedT>
+inline UnsignedT Bits::GetBitsImpl(const UnsignedT src,
+ const int offset,
+ const int nbits) {
+ const UnsignedT result = (src >> offset) & NBitsFromLSB<UnsignedT>(nbits);
+ return result;
+}
+
+template<typename UnsignedT>
+inline UnsignedT Bits::GetLowBitsImpl(const UnsignedT src, const int nbits) {
+ return GetBitsImpl(src, 0, nbits);
+}
+
+#endif // S2_UTIL_BITS_BITS_H_
--- /dev/null
+// Copyright 2000 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+//
+//
+// This holds the encoding/decoding routines that used to live in netutil
+
+#ifndef S2_UTIL_CODING_CODER_H_
+#define S2_UTIL_CODING_CODER_H_
+
+#include <cstring>
+
+// Avoid adding expensive includes here.
+#include "s2/base/casts.h"
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+#include "s2/base/port.h"
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/third_party/absl/meta/type_traits.h"
+#include "s2/util/coding/varint.h"
+#include "s2/util/endian/endian.h"
+
+/* Class for encoding data into a memory buffer */
+class Decoder;
+class Encoder {
+ public:
+ // Creates an empty Encoder with no room that is enlarged
+ // (if necessary) when "Encoder::Ensure(N)" is called.
+ Encoder();
+ ~Encoder();
+
+ // Initialize encoder to encode into "buf"
+ Encoder(void* buf, size_t maxn);
+ void reset(void* buf, size_t maxn);
+ void clear();
+
+ // Encoding routines. Note that these do not check bounds
+ void put8(unsigned char v);
+ void put16(uint16 v);
+ void put32(uint32 v);
+ void put64(uint64 v);
+ void putn(const void* mem, size_t n);
+
+ // Put no more than n bytes, stopping when c is put.
+ void putcn(const void* mem, int c, size_t n);
+
+ void puts(const void* mem); // put a c-string including \0
+ void puts_without_null(const char* mem); // put a c-string without \0
+ void putfloat(float f);
+ void putdouble(double d);
+
+ // Support for variable length encoding with 7 bits per byte
+ // (these are just simple wrappers around the Varint module)
+ static const int kVarintMax32 = Varint::kMax32;
+ static const int kVarintMax64 = Varint::kMax64;
+
+ void put_varint32(uint32 v);
+ void put_varint32_inline(uint32 v);
+ void put_varint64(uint64 v);
+ static int varint32_length(uint32 v); // Length of var encoding of "v"
+ static int varint64_length(uint64 v); // Length of var encoding of "v"
+
+ // The fast implementation of the code below with boundary checks.
+ // uint64 val;
+ // if (!dec->get_varint64(&val))
+ // return false;
+ // enc->put_varint64(val);
+ // return true;
+ // We assume that the encoder and decoder point to different buffers.
+ // If the decoder has invalid value, i.e., dec->get_varint64(&val)
+ // returns false, the decoder is not updated, which is different from
+ // dec->get_varint64(&val).
+ bool put_varint64_from_decoder(Decoder* dec);
+
+ // Return number of bytes encoded so far
+ size_t length() const;
+
+ // Return number of bytes of space remaining in buffer
+ size_t avail() const;
+
+ // REQUIRES: Encoder was created with the 0-argument constructor interface.
+ //
+ // This interface ensures that at least "N" more bytes are available
+ // in the underlying buffer by resizing the buffer (if necessary).
+ //
+ // Note that no bounds checking is done on any of the put routines,
+ // so it is the client's responsibility to call Ensure() at
+ // appropriate intervals to ensure that enough space is available
+ // for the data being added.
+ void Ensure(size_t N);
+
+ // Returns true if Ensure is allowed to be called on "this"
+ bool ensure_allowed() const { return underlying_buffer_ != nullptr; }
+
+ // Return ptr to start of encoded data. This pointer remains valid
+ // until reset or Ensure is called.
+ const char* base() const { return reinterpret_cast<const char*>(orig_); }
+
+ // Advances the write pointer by "N" bytes.
+ void skip(size_t N) { buf_ += N; }
+
+ // REQUIRES: length() >= N
+ // Removes the last N bytes out of the encoded buffer
+ void RemoveLast(size_t N);
+
+ // REQUIRES: length() >= N
+ // Removes the last length()-N bytes to make the encoded buffer have length N
+ void Resize(size_t N);
+
+ private:
+ void EnsureSlowPath(size_t N);
+
+ // Puts varint64 from decoder for varint64 sizes from 3 ~ 10. This is less
+ // common cases compared to 1 - 2 byte varint64. Returns false if either the
+ // encoder or the decoder fails the boundary check, or varint64 size exceeds
+ // the maximum size (kVarintMax64).
+ bool PutVarint64FromDecoderLessCommonSizes(Decoder* dec);
+
+ // buf_ points into the orig_ buffer, just past the last encoded byte.
+ unsigned char* buf_ = nullptr;
+
+ // limits_ points just past the last allocated byte in the orig_ buffer.
+ unsigned char* limit_ = nullptr;
+
+
+ // If this Encoder owns its buffer, underlying_buffer_ is non-nullptr
+ // and the Encoder is allowed to resize it when Ensure() is called.
+ unsigned char* underlying_buffer_ = nullptr;
+
+ // orig_ points to the start of the encoding buffer,
+ // whether or not the Encoder owns it.
+ unsigned char* orig_ = nullptr;
+
+ static unsigned char kEmptyBuffer;
+
+#ifndef SWIG
+ Encoder(Encoder const&) = delete;
+ void operator=(Encoder const&) = delete;
+#endif // SWIG
+};
+
+/* Class for decoding data from a memory buffer */
+class Decoder {
+ public:
+ // Empty constructor to create uninitialized decoder
+ inline Decoder() { }
+
+ // NOTE: for efficiency reasons, this is not virtual. so don't add
+ // any members that really need to be destructed, and be careful about
+ // inheritance.
+ // The defaulted destructor is not explicitly written to avoid confusing SWIG.
+ // ~Decoder() = default;
+
+ // Initialize decoder to decode from "buf"
+ Decoder(const void* buf, size_t maxn);
+ void reset(const void* buf, size_t maxn);
+
+ // Decoding routines. Note that these do not check bounds
+ unsigned char get8();
+ uint16 get16();
+ uint32 get32();
+ uint64 get64();
+ float getfloat();
+ double getdouble();
+ void getn(void* mem, size_t n);
+ void getcn(void* mem, int c, size_t n); // get no more than n bytes,
+ // stopping after c is got
+ void gets(void* mem, size_t n); // get a c-string no more than
+ // n bytes. always appends '\0'
+ void skip(ptrdiff_t n);
+ unsigned char const* ptr() const; // Return ptr to current position in buffer
+
+ // "get_varint" actually checks bounds
+ bool get_varint32(uint32* v);
+ bool get_varint64(uint64* v);
+
+ size_t pos() const;
+ // Return number of bytes decoded so far
+
+ size_t avail() const;
+ // Return number of available bytes to read
+
+ private:
+ friend class Encoder;
+ friend class IndexBlockDecoder;
+ const unsigned char* orig_;
+ const unsigned char* buf_;
+ const unsigned char* limit_;
+};
+
+// TODO(user): Remove when LLVM detects and optimizes this case.
+class DecoderExtensions {
+ private:
+ friend class Untranspose; // In net/proto/transpose.cc.
+ friend void TestFillArray();
+ // Fills an array of num_decoders decoders with Decoder(nullptr, 0) instances.
+ // This is much more efficient than using the stl.
+ static void FillArray(Decoder* array, int num_decoders);
+};
+
+/***** Implementation details. Clients should ignore them. *****/
+
+inline Encoder::Encoder(void* b, size_t maxn) :
+ buf_(reinterpret_cast<unsigned char*>(b)),
+ limit_(reinterpret_cast<unsigned char*>(b) + maxn),
+ orig_(reinterpret_cast<unsigned char*>(b)) { }
+
+inline void Encoder::reset(void* b, size_t maxn) {
+ orig_ = buf_ = reinterpret_cast<unsigned char*>(b);
+ limit_ = orig_ + maxn;
+ // Can't use the underlying buffer anymore
+ if (underlying_buffer_ != &kEmptyBuffer) {
+ delete[] underlying_buffer_;
+ }
+ underlying_buffer_ = nullptr;
+}
+
+inline void Encoder::clear() {
+ buf_ = orig_;
+}
+
+inline void Encoder::Ensure(size_t N) {
+ S2_DCHECK(ensure_allowed());
+ if (avail() < N) {
+ EnsureSlowPath(N);
+ }
+}
+
+inline size_t Encoder::length() const {
+ S2_DCHECK_GE(buf_, orig_);
+ S2_CHECK_LE(buf_, limit_); // Catch the buffer overflow.
+ return buf_ - orig_;
+}
+
+inline size_t Encoder::avail() const {
+ S2_DCHECK_GE(limit_, buf_);
+ return limit_ - buf_;
+}
+
+inline void Encoder::putn(const void* src, size_t n) {
+ memcpy(buf_, src, n);
+ buf_ += n;
+}
+
+inline void Encoder::putcn(const void* src, int c, size_t n) {
+ unsigned char *old = buf_;
+ buf_ = static_cast<unsigned char *>(memccpy(buf_, src, c, n));
+ if (buf_ == nullptr)
+ buf_ = old + n;
+}
+
+inline void Encoder::puts(const void* src) {
+ putcn(src, '\0', avail());
+}
+
+inline void Encoder::puts_without_null(const char* mem) {
+ while (*mem != '\0' && buf_ < limit_) {
+ *buf_++ = *mem++;
+ }
+}
+
+inline void Encoder::put_varint32(uint32 v) {
+ buf_ = reinterpret_cast<unsigned char*>
+ (Varint::Encode32(reinterpret_cast<char*>(buf_), v));
+}
+
+inline void Encoder::put_varint32_inline(uint32 v) {
+ buf_ = reinterpret_cast<unsigned char*>
+ (Varint::Encode32Inline(reinterpret_cast<char*>(buf_), v));
+}
+
+inline void Encoder::put_varint64(uint64 v) {
+ buf_ = reinterpret_cast<unsigned char*>
+ (Varint::Encode64(reinterpret_cast<char*>(buf_), v));
+}
+
+// Copies N bytes from *src to *dst then advances both pointers by N bytes.
+// Template parameter N specifies the number of bytes to copy. Passing
+// constant size results in optimized code from memcpy for the size.
+template <size_t N>
+void CopyAndAdvance(const uint8** src, uint8** dst) {
+ memcpy(*dst, *src, N);
+ *dst += N;
+ *src += N;
+}
+
+// Tries a fast path if both the decoder and the encoder have enough room for
+// max varint64 (10 bytes). With enough room, we don't need boundary checks at
+// every iterations. Also, memcpy with known size is faster than copying a byte
+// at a time (e.g. one movq vs. eight movb's).
+//
+// If either the decoder or the encoder doesn't have enough room, it falls back
+// to previous example where copy and boundary check happen at every byte.
+inline bool Encoder::PutVarint64FromDecoderLessCommonSizes(Decoder* dec) {
+ const unsigned char* dec_ptr = dec->buf_;
+ const unsigned char* dec_limit = dec->limit_;
+
+ // Check once if both the encoder and the decoder have enough room for
+ // maximum varint64 (kVarintMax64) instead of checking at every bytes.
+ if (ABSL_PREDICT_TRUE(dec_ptr <= dec_limit - kVarintMax64 &&
+ buf_ <= limit_ - kVarintMax64)) {
+ if (dec_ptr[2] < 128) {
+ CopyAndAdvance<3>(&dec->buf_, &buf_);
+ } else if (dec_ptr[3] < 128) {
+ CopyAndAdvance<4>(&dec->buf_, &buf_);
+ } else if (dec_ptr[4] < 128) {
+ CopyAndAdvance<5>(&dec->buf_, &buf_);
+ } else if (dec_ptr[5] < 128) {
+ CopyAndAdvance<6>(&dec->buf_, &buf_);
+ } else if (dec_ptr[6] < 128) {
+ CopyAndAdvance<7>(&dec->buf_, &buf_);
+ } else if (dec_ptr[7] < 128) {
+ CopyAndAdvance<8>(&dec->buf_, &buf_);
+ } else if (dec_ptr[8] < 128) {
+ CopyAndAdvance<9>(&dec->buf_, &buf_);
+ } else if (dec_ptr[9] < 2) {
+ // 10th byte stores at most 1 bit for varint64.
+ CopyAndAdvance<10>(&dec->buf_, &buf_);
+ } else {
+ return false;
+ }
+ return true;
+ }
+
+ unsigned char c;
+ unsigned char* enc_ptr = buf_;
+
+ // The loop executes at most (kVarintMax64 - 1) iterations because either the
+ // decoder or the encoder has less availability than kVarintMax64. We must be
+ // careful about the cost of moving any computation out of the loop.
+ // Xref cl/133546957 for more details of various implementations we explored.
+ do {
+ if (dec_ptr >= dec_limit) return false;
+ if (enc_ptr >= limit_) return false;
+ c = *dec_ptr;
+ *enc_ptr = c;
+ ++dec_ptr;
+ ++enc_ptr;
+ } while (c >= 128);
+
+ dec->buf_ = dec_ptr;
+ buf_ = enc_ptr;
+ return true;
+}
+
+// The fast implementation of the code below with boundary checks.
+// uint64 val;
+// if (!dec->get_varint64(&val))
+// return false;
+// enc->put_varint64(val);
+// return true;
+// BM_getvarfrom* in coder_unittest.cc are the benchmarks that measure the
+// performance of different implementations.
+//
+// Handles varint64 with one to two bytes separately as a common case.
+// PutVarint64FromDecoderLessCommonSizes handles the remaining sizes. To avoid
+// over-inlining, PutVarint64FromDecoderLessCommonSizes is defined in coder.cc.
+// As Untranspose::DecodeMessage is the only caller, compiler should be able to
+// inline all if necessary.
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline bool Encoder::put_varint64_from_decoder(
+ Decoder* dec) {
+ unsigned char* enc_ptr = buf_;
+ const unsigned char* dec_ptr = dec->buf_;
+
+ // Common cases to handle varint64 with one to two bytes.
+ if (ABSL_PREDICT_TRUE(dec_ptr < dec->limit_ && dec_ptr[0] < 128)) {
+ if (ABSL_PREDICT_FALSE(enc_ptr >= limit_)) {
+ return false;
+ }
+ *enc_ptr = *dec_ptr;
+ dec->buf_++;
+ buf_++;
+ return true;
+ }
+
+ if (dec_ptr < dec->limit_ - 1 && dec_ptr[1] < 128) {
+ if (ABSL_PREDICT_FALSE(enc_ptr >= limit_ - 1)) {
+ return false;
+ }
+ UNALIGNED_STORE16(enc_ptr, UNALIGNED_LOAD16(dec_ptr));
+ dec->buf_ += 2;
+ buf_ += 2;
+ return true;
+ }
+
+ // For less common sizes in [3, kVarintMax64].
+ return PutVarint64FromDecoderLessCommonSizes(dec);
+}
+
+inline Decoder::Decoder(const void* b, size_t maxn) {
+ reset(b, maxn);
+}
+
+inline void Decoder::reset(const void* b, size_t maxn) {
+ orig_ = buf_ = reinterpret_cast<const unsigned char*>(b);
+ limit_ = orig_ + maxn;
+}
+
+inline size_t Decoder::pos() const {
+ S2_DCHECK_GE(buf_, orig_);
+ return buf_ - orig_;
+}
+
+inline size_t Decoder::avail() const {
+ S2_DCHECK_GE(limit_, buf_);
+ return limit_ - buf_;
+}
+
+inline void Decoder::getn(void* dst, size_t n) {
+ memcpy(dst, buf_, n);
+ buf_ += n;
+}
+
+inline void Decoder::getcn(void* dst, int c, size_t n) {
+ void *ptr;
+ ptr = memccpy(dst, buf_, c, n);
+ if (ptr == nullptr)
+ buf_ = buf_ + n;
+ else
+ buf_ = buf_ + (reinterpret_cast<unsigned char *>(ptr) -
+ reinterpret_cast<unsigned char *>(dst));
+}
+
+inline void Decoder::gets(void* dst, size_t n) {
+ size_t len = n - 1;
+ S2_DCHECK_GE(limit_, buf_);
+ if (n > static_cast<size_t>(1 + limit_ - buf_)) {
+ len = limit_ - buf_;
+ }
+ (reinterpret_cast<char *>(dst))[len] = '\0';
+ getcn(dst, '\0', len);
+}
+
+inline void Decoder::skip(ptrdiff_t n) {
+ buf_ += n;
+}
+
+inline unsigned char const* Decoder::ptr() const {
+ return buf_;
+}
+
+inline void DecoderExtensions::FillArray(Decoder* array, int num_decoders) {
+ // This is an optimization based on the fact that Decoder(nullptr, 0) sets all
+ // structure bytes to 0. This is valid because Decoder is TriviallyCopyable
+ // (https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable).
+ static_assert(absl::is_trivially_copy_constructible<Decoder>::value,
+ "Decoder must be trivially copy-constructible");
+ static_assert(absl::is_trivially_copy_assignable<Decoder>::value,
+ "Decoder must be trivially copy-assignable");
+ static_assert(absl::is_trivially_destructible<Decoder>::value,
+ "Decoder must be trivially destructible");
+
+ // on R 4.0 on Windows, this line gives install warning
+ // warning: 'void* memset(void*, int, size_t)' clearing an object of non-trivial type 'class Decoder';
+ // use assignment or value-initialization instead [-Wclass-memaccess]
+ // std::memset(array, 0, num_decoders * sizeof(Decoder));
+ // using non-optimized version as suggested above (this is not called by R code)
+ for (int i = 0; i < num_decoders; i++) {
+ Decoder* decoder = array + i;
+ decoder->reset(nullptr, 0);
+ }
+}
+
+inline void Encoder::put8(unsigned char v) {
+ S2_DCHECK_GE(avail(), sizeof(v));
+ *buf_ = v;
+ buf_ += sizeof(v);
+}
+
+inline void Encoder::put16(uint16 v) {
+ S2_DCHECK_GE(avail(), sizeof(v));
+ LittleEndian::Store16(buf_, v);
+ buf_ += sizeof(v);
+}
+
+inline void Encoder::put32(uint32 v) {
+ S2_DCHECK_GE(avail(), sizeof(v));
+ LittleEndian::Store32(buf_, v);
+ buf_ += sizeof(v);
+}
+
+inline void Encoder::put64(uint64 v) {
+ S2_DCHECK_GE(avail(), sizeof(v));
+ LittleEndian::Store64(buf_, v);
+ buf_ += sizeof(v);
+}
+
+inline void Encoder::putfloat(float f) {
+ put32(absl::bit_cast<uint32>(f));
+}
+
+inline void Encoder::putdouble(double d) {
+ put64(absl::bit_cast<uint64>(d));
+}
+
+inline unsigned char Decoder::get8() {
+ const unsigned char v = *buf_;
+ buf_ += sizeof(v);
+ return v;
+}
+
+inline uint16 Decoder::get16() {
+ const uint16 v = LittleEndian::Load16(buf_);
+ buf_ += sizeof(v);
+ return v;
+}
+
+inline uint32 Decoder::get32() {
+ const uint32 v = LittleEndian::Load32(buf_);
+ buf_ += sizeof(v);
+ return v;
+}
+
+inline uint64 Decoder::get64() {
+ const uint64 v = LittleEndian::Load64(buf_);
+ buf_ += sizeof(v);
+ return v;
+}
+
+inline float Decoder::getfloat() {
+ return absl::bit_cast<float>(get32());
+}
+
+inline double Decoder::getdouble() {
+ return absl::bit_cast<double>(get64());
+}
+
+inline bool Decoder::get_varint32(uint32* v) {
+ const char* const r =
+ Varint::Parse32WithLimit(reinterpret_cast<const char*>(buf_),
+ reinterpret_cast<const char*>(limit_), v);
+ if (r == nullptr) {
+ return false;
+ }
+ buf_ = reinterpret_cast<const unsigned char*>(r);
+ return true;
+}
+
+inline bool Decoder::get_varint64(uint64* v) {
+ const char* const r =
+ Varint::Parse64WithLimit(reinterpret_cast<const char*>(buf_),
+ reinterpret_cast<const char*>(limit_), v);
+ if (r == nullptr) {
+ return false;
+ }
+ buf_ = reinterpret_cast<const unsigned char*>(r);
+ return true;
+}
+
+#endif // S2_UTIL_CODING_CODER_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+//
+// far-far-superior implementation courtesy of amc@google.com (Adam Costello)
+//
+// Nth Derivative Coding
+// (In signal processing disciplines, this is known as N-th Delta Coding.)
+//
+// Good for varint coding integer sequences with polynomial trends.
+//
+// Instead of coding a sequence of values directly, code its nth-order discrete
+// derivative. Overflow in integer addition and subtraction makes this a
+// lossless transform.
+//
+// constant linear quadratic
+// trend trend trend
+// / \ / \ / \_
+// input |0 0 0 0 1 2 3 4 9 16 25 36
+// 0th derivative(identity) |0 0 0 0 1 2 3 4 9 16 25 36
+// 1st derivative(delta coding) | 0 0 0 1 1 1 1 5 7 9 11
+// 2nd derivative(linear prediction) | 0 0 1 0 0 0 4 2 2 2
+// -------------------------------------
+// 0 1 2 3 4 5 6 7 8 9 10 11
+// n in sequence
+//
+// Higher-order codings can break even or be detrimental on other sequences.
+//
+// random oscillating
+// / \ / \_
+// input |5 9 6 1 8 8 2 -2 4 -4 6 -6
+// 0th derivative(identity) |5 9 6 1 8 8 2 -2 4 -4 6 -6
+// 1st derivative(delta coding) | 4 -3 -5 7 0 -6 -4 6 -8 10 -12
+// 2nd derivative(linear prediction) | -7 -2 12 -7 -6 2 10 -14 18 -22
+// ---------------------------------------
+// 0 1 2 3 4 5 6 7 8 9 10 11
+// n in sequence
+//
+// Note that the nth derivative isn't available until sequence item n. Earlier
+// values are coded at lower order. For the above table, read 5 4 -7 -2 12 ...
+//
+// A caveat on class usage. Encode() and Decode() share state. Using both
+// without a Reset() in-between probably doesn't make sense.
+
+#ifndef S2_UTIL_CODING_NTH_DERIVATIVE_H_
+#define S2_UTIL_CODING_NTH_DERIVATIVE_H_
+
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+
+class NthDerivativeCoder {
+ public:
+ // range of supported Ns: [ N_MIN, N_MAX ]
+ enum {
+ N_MIN = 0,
+ N_MAX = 10,
+ };
+
+ // Initialize a new NthDerivativeCoder of the given N.
+ explicit NthDerivativeCoder(int n);
+
+ // Encode the next value in the sequence. Don't mix with Decode() calls.
+ int32 Encode(int32 k);
+
+ // Decode the next value in the sequence. Don't mix with Encode() calls.
+ int32 Decode(int32 k);
+
+ // Reset state.
+ void Reset();
+
+ // accessors
+ int n() const { return n_; }
+
+ private:
+ int n_; // derivative order of the coder (the N in NthDerivative)
+ int m_; // the derivative order in which to code the next value(ramps to n_)
+ int32 memory_[N_MAX]; // value memory. [0] is oldest
+};
+
+// Implementation below. Callers Ignore.
+//
+// Inlining the implementation roughly doubled the speed. All other
+// optimization tricks failed miserably.
+
+#if ~0 != -1
+#error Sorry, this code needs twos complement integers.
+#endif
+
+inline NthDerivativeCoder::NthDerivativeCoder(int n) : n_(n) {
+ if (n < N_MIN || n > N_MAX) {
+ S2_LOG(ERROR) << "Unsupported N: " << n << ". Using 0 instead.";
+ n_ = 0;
+ }
+ Reset();
+}
+
+inline int32 NthDerivativeCoder::Encode(int32 k) {
+ for (int i = 0; i < m_; ++i) {
+ uint32 delta = static_cast<uint32>(k) - memory_[i];
+ memory_[i] = k;
+ k = delta;
+ }
+ if (m_ < n_)
+ memory_[m_++] = k;
+ return k;
+}
+
+inline int32 NthDerivativeCoder::Decode(int32 k) {
+ if (m_ < n_)
+ m_++;
+ for (int i = m_ - 1; i >= 0; --i)
+ k = memory_[i] = memory_[i] + static_cast<uint32>(k);
+ return k;
+}
+
+inline void NthDerivativeCoder::Reset() {
+ for (int i = 0; i < n_; ++i)
+ memory_[i] = 0;
+ m_ = 0;
+}
+
+#endif // S2_UTIL_CODING_NTH_DERIVATIVE_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+//
+//
+// Data transforms that can help code more efficiently.
+
+#ifndef S2_UTIL_CODING_TRANSFORMS_H_
+#define S2_UTIL_CODING_TRANSFORMS_H_
+
+#include "s2/base/integral_types.h"
+
+// ZigZag Transform
+//
+// Good for varint coding small signed integers centered around 0.
+//
+// int32 -> uint32
+// -------------------------
+// 0 -> 0
+// -1 -> 1
+// 1 -> 2
+// -2 -> 3
+// ... -> ...
+// 2147483647 -> 4294967294
+// -2147483648 -> 4294967295
+//
+// >> encode >>
+// << decode <<
+
+static inline uint32 ZigZagEncode(int32 n) {
+ // We need the cast to avoid an arithmetic shift.
+ uint32 sign = (static_cast<uint32>(n)) >> 31;
+ return (static_cast<uint32>(n) << 1) ^ (0u - sign);
+}
+
+static inline int32 ZigZagDecode(uint32 n) {
+ return (n >> 1) ^ (0u - (n & 1));
+}
+
+static inline uint64 ZigZagEncode64(int64 n) {
+ // We need the cast to avoid an arithmetic shift.
+ uint64 sign = (static_cast<uint64>(n)) >> 63;
+ return (static_cast<uint64>(n) << 1) ^ (0u - sign);
+}
+
+static inline int64 ZigZagDecode64(uint64 n) {
+ return (n >> 1) ^ (0u - (n & 1));
+}
+
+#endif // S2_UTIL_CODING_TRANSFORMS_H_
--- /dev/null
+// Copyright 2001 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+//
+// Raw support for varint encoding. Higher level interfaces are
+// provided by Encoder/Decoder/IOBuffer. Clients should typically use
+// those interfaces, unless speed is paramount.
+//
+// If decoding speed is very important, consider using PrefixVarint instead.
+// It has the same compression ratio, but generally faster decoding.
+//
+// Provided routines:
+// vi_parse32_unchecked
+// vi_parse64_unchecked
+// vi_encode32_unchecked
+// vi_encode64_unchecked
+
+#ifndef S2_UTIL_CODING_VARINT_H_
+#define S2_UTIL_CODING_VARINT_H_
+
+// Avoid adding expensive includes here.
+#include <cassert>
+#include <cstddef>
+#include <string>
+
+#include "s2/base/integral_types.h"
+#include "s2/base/port.h"
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/util/bits/bits.h"
+
+// Just a namespace, not a real class
+class Varint {
+ public:
+ // Maximum lengths of varint encoding of uint32 and uint64
+ static const int kMax32 = 5;
+ static const int kMax64 = 10;
+
+ // The decoder does not read past the end of the encoded data.
+ static const int kSlopBytes = 0;
+
+ // REQUIRES "ptr" points to a buffer of length at least kMaxXX
+ // EFFECTS Scan next varint from "ptr" and store in OUTPUT.
+ // Returns pointer just past last read byte. Returns
+ // nullptr if a valid varint value was not found.
+ static const char* Parse32(const char* ptr, uint32* OUTPUT);
+ static const char* Parse64(const char* ptr, uint64* OUTPUT);
+
+ // A fully inlined version of Parse32: useful in the most time critical
+ // routines, but its code size is large
+ static const char* Parse32Inline(const char* ptr, uint32* OUTPUT);
+
+ // REQUIRES "ptr" points just past the last byte of a varint-encoded value.
+ // REQUIRES A second varint must be encoded just before the one we parse,
+ // OR "base" must point to the first byte of the one we parse.
+ // REQUIRES Bytes [base, ptr-1] are readable
+ //
+ // EFFECTS Scan backwards from "ptr" and store in OUTPUT. Stop at the last
+ // byte of the previous varint, OR at "base", whichever one comes
+ // first. Returns pointer to the first byte of the decoded varint
+ // nullptr if a valid varint value was not found.
+ static const char* Parse32Backward(const char* ptr, const char* base,
+ uint32* OUTPUT);
+ static const char* Parse64Backward(const char* ptr, const char* base,
+ uint64* OUTPUT);
+
+ // Attempts to parse a varint32 from a prefix of the bytes in [ptr,limit-1].
+ // Never reads a character at or beyond limit. If a valid/terminated varint32
+ // was found in the range, stores it in *OUTPUT and returns a pointer just
+ // past the last byte of the varint32. Else returns nullptr. On success,
+ // "result <= limit".
+ static const char* Parse32WithLimit(const char* ptr, const char* limit,
+ uint32* OUTPUT);
+ static const char* Parse64WithLimit(const char* ptr, const char* limit,
+ uint64* OUTPUT);
+
+ // REQUIRES "ptr" points to the first byte of a varint-encoded value.
+ // EFFECTS Scans until the end of the varint and returns a pointer just
+ // past the last byte. Returns nullptr if "ptr" does not point to
+ // a valid varint value.
+ static const char* Skip32(const char* ptr);
+ static const char* Skip64(const char* ptr);
+
+ // REQUIRES "ptr" points just past the last byte of a varint-encoded value.
+ // REQUIRES A second varint must be encoded just before the one we parse,
+ // OR "base" must point to the first byte of the one we parse.
+ // REQUIRES Bytes [base, ptr-1] are readable
+ //
+ // EFFECTS Scan backwards from "ptr" and stop at the last byte of the
+ // previous varint, OR at "base", whichever one comes first.
+ // Returns pointer to the first byte of the skipped varint or
+ // nullptr if a valid varint value was not found.
+ static const char* Skip32Backward(const char* ptr, const char* base);
+ static const char* Skip64Backward(const char* ptr, const char* base);
+
+ // REQUIRES "ptr" points to a buffer of length sufficient to hold "v".
+ // EFFECTS Encodes "v" into "ptr" and returns a pointer to the
+ // byte just past the last encoded byte.
+ static char* Encode32(char* ptr, uint32 v);
+ static char* Encode64(char* ptr, uint64 v);
+
+ // A fully inlined version of Encode32: useful in the most time critical
+ // routines, but its code size is large
+ static char* Encode32Inline(char* ptr, uint32 v);
+
+ // EFFECTS Returns the encoding length of the specified value.
+ static int Length32(uint32 v);
+ static int Length64(uint64 v);
+
+ // EFFECTS Appends the varint representation of "value" to "*s".
+ static void Append32(string* s, uint32 value);
+ static void Append64(string* s, uint64 value);
+
+ // EFFECTS Encodes a pair of values to "*s". The encoding
+ // is done by weaving together 4 bit groups of
+ // each number into a single 64 bit value, and then
+ // encoding this value as a Varint64 value. This means
+ // that if both a and b are small, both values can be
+ // encoded in a single byte.
+ ABSL_DEPRECATED("Use TwoValuesVarint::Encode32.")
+ static void EncodeTwo32Values(string* s, uint32 a, uint32 b);
+ ABSL_DEPRECATED("Use TwoValuesVarint::Decode32.")
+ static const char* DecodeTwo32Values(const char* ptr, uint32* a, uint32* b);
+ ABSL_DEPRECATED("Use TwoValuesVarint::Decode32WithLimit.")
+ static const char* DecodeTwo32ValuesWithLimit(const char* ptr,
+ const char* limit, uint32* a,
+ uint32* b);
+
+ // Decode and sum up a sequence of deltas until the sum >= goal.
+ // It is significantly faster than calling ParseXXInline in a loop.
+ // NOTE(user): The code does NO error checking, it assumes all the
+ // deltas are valid and the sum of deltas will never exceed
+ // numeric_limits<int64>::max(). The code works for both 32bits and
+ // 64bits varint, and on 64 bits machines, the 64 bits version is
+ // almost always faster. Thus we only have a 64 bits interface here.
+ // The interface is slightly different from the other functions in that
+ // it requires *signed* integers.
+ // REQUIRES "ptr" points to the first byte of a varint-encoded delta.
+ // The sum of deltas >= goal (the code does NO boundary check).
+ // goal is positive and fit into a signed int64.
+ // EFFECTS Returns a pointer just past last read byte.
+ // "out" stores the actual sum.
+ static const char* FastDecodeDeltas(const char* ptr, int64 goal, int64* out);
+
+ private:
+ static const char* Parse32FallbackInline(const char* p, uint32* val);
+ static const char* Parse32Fallback(const char* p, uint32* val);
+ static const char* Parse64Fallback(const char* p, uint64* val);
+
+ static char* Encode32Fallback(char* ptr, uint32 v);
+
+ static const char* Parse32BackwardSlow(const char* ptr, const char* base,
+ uint32* OUTPUT);
+ static const char* Parse64BackwardSlow(const char* ptr, const char* base,
+ uint64* OUTPUT);
+ static const char* Skip32BackwardSlow(const char* ptr, const char* base);
+ static const char* Skip64BackwardSlow(const char* ptr, const char* base);
+
+ static void Append32Slow(string* s, uint32 value);
+ static void Append64Slow(string* s, uint64 value);
+
+};
+
+/***** Implementation details; clients should ignore *****/
+
+inline const char* Varint::Parse32FallbackInline(const char* p,
+ uint32* OUTPUT) {
+ // Fast path
+ const unsigned char* ptr = reinterpret_cast<const unsigned char*>(p);
+ uint32 byte, result;
+ byte = *(ptr++); result = byte & 127;
+ assert(byte >= 128); // Already checked in inlined prelude
+ byte = *(ptr++); result |= (byte & 127) << 7; if (byte < 128) goto done;
+ byte = *(ptr++); result |= (byte & 127) << 14; if (byte < 128) goto done;
+ byte = *(ptr++); result |= (byte & 127) << 21; if (byte < 128) goto done;
+ byte = *(ptr++); result |= (byte & 127) << 28; if (byte < 16) goto done;
+ return nullptr; // Value is too long to be a varint32
+ done:
+ *OUTPUT = result;
+ return reinterpret_cast<const char*>(ptr);
+}
+
+inline const char* Varint::Parse32(const char* p, uint32* OUTPUT) {
+ // Fast path for inlining
+ const unsigned char* ptr = reinterpret_cast<const unsigned char*>(p);
+ uint32 byte = *ptr;
+ if (byte < 128) {
+ *OUTPUT = byte;
+ return reinterpret_cast<const char*>(ptr) + 1;
+ } else {
+ return Parse32Fallback(p, OUTPUT);
+ }
+}
+
+inline const char* Varint::Parse32Inline(const char* p, uint32* OUTPUT) {
+ // Fast path for inlining
+ const unsigned char* ptr = reinterpret_cast<const unsigned char*>(p);
+ uint32 byte = *ptr;
+ if (byte < 128) {
+ *OUTPUT = byte;
+ return reinterpret_cast<const char*>(ptr) + 1;
+ } else {
+ return Parse32FallbackInline(p, OUTPUT);
+ }
+}
+
+inline const char* Varint::Skip32(const char* p) {
+ const unsigned char* ptr = reinterpret_cast<const unsigned char*>(p);
+ if (*ptr++ < 128) return reinterpret_cast<const char*>(ptr);
+ if (*ptr++ < 128) return reinterpret_cast<const char*>(ptr);
+ if (*ptr++ < 128) return reinterpret_cast<const char*>(ptr);
+ if (*ptr++ < 128) return reinterpret_cast<const char*>(ptr);
+ if (*ptr++ < 16) return reinterpret_cast<const char*>(ptr);
+ return nullptr; // value is too long to be a varint32
+}
+
+inline const char* Varint::Parse32Backward(const char* p, const char* base,
+ uint32* OUTPUT) {
+ if (p > base + kMax32) {
+ // Fast path
+ const unsigned char* ptr = reinterpret_cast<const unsigned char*>(p);
+ uint32 byte, result;
+ byte = *(--ptr); if (byte > 127) return nullptr;
+ result = byte;
+ byte = *(--ptr); if (byte < 128) goto done;
+ result <<= 7; result |= (byte & 127);
+ byte = *(--ptr); if (byte < 128) goto done;
+ result <<= 7; result |= (byte & 127);
+ byte = *(--ptr); if (byte < 128) goto done;
+ result <<= 7; result |= (byte & 127);
+ byte = *(--ptr); if (byte < 128) goto done;
+ result <<= 7; result |= (byte & 127);
+ byte = *(--ptr); if (byte < 128) goto done;
+ return nullptr; // Value is too long to be a varint32
+ done:
+ *OUTPUT = result;
+ return reinterpret_cast<const char*>(ptr+1);
+ } else {
+ return Parse32BackwardSlow(p, base, OUTPUT);
+ }
+}
+
+inline const char* Varint::Skip32Backward(const char* p, const char* base) {
+ if (p > base + kMax32) {
+ const unsigned char* ptr = reinterpret_cast<const unsigned char*>(p);
+ if (*(--ptr) > 127) return nullptr;
+ if (*(--ptr) < 128) return reinterpret_cast<const char*>(ptr+1);
+ if (*(--ptr) < 128) return reinterpret_cast<const char*>(ptr+1);
+ if (*(--ptr) < 128) return reinterpret_cast<const char*>(ptr+1);
+ if (*(--ptr) < 128) return reinterpret_cast<const char*>(ptr+1);
+ if (*(--ptr) < 128) return reinterpret_cast<const char*>(ptr+1);
+ return nullptr; // value is too long to be a varint32
+ } else {
+ return Skip32BackwardSlow(p, base);
+ }
+}
+
+inline const char* Varint::Parse32WithLimit(const char* p,
+ const char* l,
+ uint32* OUTPUT) {
+ // Version with bounds checks.
+ // This formerly had an optimization to inline the non-bounds checking Parse32
+ // but it was found to be slower than the straightforward implementation.
+ const unsigned char* ptr = reinterpret_cast<const unsigned char*>(p);
+ const unsigned char* limit = reinterpret_cast<const unsigned char*>(l);
+ uint32 b, result;
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result = b & 127; if (b < 128) goto done;
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result |= (b & 127) << 7; if (b < 128) goto done;
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result |= (b & 127) << 14; if (b < 128) goto done;
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result |= (b & 127) << 21; if (b < 128) goto done;
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result |= (b & 127) << 28; if (b < 16) goto done;
+ return nullptr; // Value is too long to be a varint32
+ done:
+ *OUTPUT = result;
+ return reinterpret_cast<const char*>(ptr);
+}
+
+inline const char* Varint::Parse64(const char* p, uint64* OUTPUT) {
+ const unsigned char* ptr = reinterpret_cast<const unsigned char*>(p);
+ uint32 byte = *ptr;
+ if (byte < 128) {
+ *OUTPUT = byte;
+ return reinterpret_cast<const char*>(ptr) + 1;
+ } else {
+ return Parse64Fallback(p, OUTPUT);
+ }
+}
+
+inline const char* Varint::Skip64(const char* p) {
+ const unsigned char* ptr = reinterpret_cast<const unsigned char*>(p);
+ if (*ptr++ < 128) return reinterpret_cast<const char*>(ptr);
+ if (*ptr++ < 128) return reinterpret_cast<const char*>(ptr);
+ if (*ptr++ < 128) return reinterpret_cast<const char*>(ptr);
+ if (*ptr++ < 128) return reinterpret_cast<const char*>(ptr);
+ if (*ptr++ < 128) return reinterpret_cast<const char*>(ptr);
+ if (*ptr++ < 128) return reinterpret_cast<const char*>(ptr);
+ if (*ptr++ < 128) return reinterpret_cast<const char*>(ptr);
+ if (*ptr++ < 128) return reinterpret_cast<const char*>(ptr);
+ if (*ptr++ < 128) return reinterpret_cast<const char*>(ptr);
+ if (*ptr++ < 2) return reinterpret_cast<const char*>(ptr);
+ return nullptr; // value is too long to be a varint64
+}
+
+inline const char* Varint::Parse64Backward(const char* p, const char* b,
+ uint64* OUTPUT) {
+ if (p > b + kMax64) {
+ // Fast path
+ const unsigned char* ptr = reinterpret_cast<const unsigned char*>(p);
+ uint32 byte;
+ uint64 res;
+
+ byte = *(--ptr); if (byte > 127) return nullptr;
+
+ res = byte;
+ byte = *(--ptr); if (byte < 128) goto done;
+ res <<= 7; res |= (byte & 127);
+ byte = *(--ptr); if (byte < 128) goto done;
+ res <<= 7; res |= (byte & 127);
+ byte = *(--ptr); if (byte < 128) goto done;
+ res <<= 7; res |= (byte & 127);
+ byte = *(--ptr); if (byte < 128) goto done;
+ res <<= 7; res |= (byte & 127);
+ byte = *(--ptr); if (byte < 128) goto done;
+ res <<= 7; res |= (byte & 127);
+ byte = *(--ptr); if (byte < 128) goto done;
+ res <<= 7; res |= (byte & 127);
+ byte = *(--ptr); if (byte < 128) goto done;
+ res <<= 7; res |= (byte & 127);
+ byte = *(--ptr); if (byte < 128) goto done;
+ res <<= 7; res |= (byte & 127);
+ byte = *(--ptr); if (byte < 128) goto done;
+ res <<= 7; res |= (byte & 127);
+ byte = *(--ptr); if (byte < 128) goto done;
+
+ return nullptr; // Value is too long to be a varint64
+
+ done:
+ *OUTPUT = res;
+ return reinterpret_cast<const char*>(ptr + 1);
+ } else {
+ return Parse64BackwardSlow(p, b, OUTPUT);
+ }
+}
+
+inline const char* Varint::Skip64Backward(const char* p, const char* b) {
+ if (p > b + kMax64) {
+ // Fast path
+ const unsigned char* ptr = reinterpret_cast<const unsigned char*>(p);
+ if (*(--ptr) > 127) return nullptr;
+ if (*(--ptr) < 128) return reinterpret_cast<const char*>(ptr+1);
+ if (*(--ptr) < 128) return reinterpret_cast<const char*>(ptr+1);
+ if (*(--ptr) < 128) return reinterpret_cast<const char*>(ptr+1);
+ if (*(--ptr) < 128) return reinterpret_cast<const char*>(ptr+1);
+ if (*(--ptr) < 128) return reinterpret_cast<const char*>(ptr+1);
+ if (*(--ptr) < 128) return reinterpret_cast<const char*>(ptr+1);
+ if (*(--ptr) < 128) return reinterpret_cast<const char*>(ptr+1);
+ if (*(--ptr) < 128) return reinterpret_cast<const char*>(ptr+1);
+ if (*(--ptr) < 128) return reinterpret_cast<const char*>(ptr+1);
+ if (*(--ptr) < 128) return reinterpret_cast<const char*>(ptr+1);
+ return nullptr; // value is too long to be a varint64
+ } else {
+ return Skip64BackwardSlow(p, b);
+ }
+}
+
+inline int Varint::Length32(uint32 v) {
+ // This computes value == 0 ? 1 : floor(log2(v)) / 7 + 1
+ // Use an explicit multiplication to implement the divide of
+ // a number in the 1..31 range.
+ // Explicit OR 0x1 to handle v == 0.
+ uint32 log2value = Bits::Log2FloorNonZero(v | 0x1);
+ return static_cast<int>((log2value * 9 + 73) / 64);
+}
+
+inline int Varint::Length64(uint64 v) {
+ // This computes value == 0 ? 1 : floor(log2(v)) / 7 + 1
+ // Use an explicit multiplication to implement the divide of
+ // a number in the 1..63 range.
+ // Explicit OR 0x1 to handle v == 0.
+ uint32 log2value = Bits::Log2FloorNonZero64(v | 0x1);
+ return static_cast<int>((log2value * 9 + 73) / 64);
+}
+
+inline void Varint::Append32(string* s, uint32 value) {
+ // Inline the fast-path for single-character output, but fall back to the .cc
+ // file for the full version. The size<capacity check is so the compiler can
+ // optimize out the string resize code.
+ if (value < 128 && s->size() < s->capacity()) {
+ s->push_back((unsigned char)value);
+ } else {
+ Append32Slow(s, value);
+ }
+}
+
+inline void Varint::Append64(string* s, uint64 value) {
+ // Inline the fast-path for single-character output, but fall back to the .cc
+ // file for the full version. The size<capacity check is so the compiler can
+ // optimize out the string resize code.
+ if (value < 128 && s->size() < s->capacity()) {
+ s->push_back((unsigned char)value);
+ } else {
+ Append64Slow(s, value);
+ }
+}
+
+inline char* Varint::Encode32Inline(char* sptr, uint32 v) {
+ // Operate on characters as unsigneds
+ uint8* ptr = reinterpret_cast<uint8*>(sptr);
+ static const uint32 B = 128;
+ if (v < (1<<7)) {
+ *(ptr++) = static_cast<uint8>(v);
+ } else if (v < (1<<14)) {
+ *(ptr++) = static_cast<uint8>(v | B);
+ *(ptr++) = static_cast<uint8>(v>>7);
+ } else if (v < (1<<21)) {
+ *(ptr++) = static_cast<uint8>(v | B);
+ *(ptr++) = static_cast<uint8>((v>>7) | B);
+ *(ptr++) = static_cast<uint8>(v>>14);
+ } else if (v < (1<<28)) {
+ *(ptr++) = static_cast<uint8>(v | B);
+ *(ptr++) = static_cast<uint8>((v>>7) | B);
+ *(ptr++) = static_cast<uint8>((v>>14) | B);
+ *(ptr++) = static_cast<uint8>(v>>21);
+ } else {
+ *(ptr++) = static_cast<uint8>(v | B);
+ *(ptr++) = static_cast<uint8>((v>>7) | B);
+ *(ptr++) = static_cast<uint8>((v>>14) | B);
+ *(ptr++) = static_cast<uint8>((v>>21) | B);
+ *(ptr++) = static_cast<uint8>(v>>28);
+ }
+ return reinterpret_cast<char*>(ptr);
+}
+
+#if (-1 >> 1) != -1
+#error FastDecodeDeltas() needs right-shift to sign-extend.
+#endif
+inline const char* Varint::FastDecodeDeltas(const char* ptr,
+ int64 goal,
+ int64* out) {
+ int64 value;
+ int64 sum = - goal;
+ int64 shift = 0;
+ // Make decoding faster by eliminating unpredictable branching.
+ do {
+ value = static_cast<int8>(*ptr++); // sign extend one byte of data
+ sum += (value & 0x7F) << shift;
+ shift += 7;
+ // (value >> 7) is either -1(continuation byte) or 0 (stop byte)
+ shift &= value >> 7;
+ // Loop if we haven't reached goal (sum < 0) or we haven't finished
+ // parsing current delta (value < 0). We write it in the form of
+ // (a | b) < 0 as opposed to (a < 0 || b < 0) as the former one is
+ // usually as fast as a test for (a < 0).
+ } while ((sum | value) < 0);
+
+ *out = goal + sum;
+ return ptr;
+}
+
+#endif // S2_UTIL_CODING_VARINT_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+//
+// Utility functions that depend on bytesex. We define htonll and ntohll,
+// as well as "Google" versions of all the standards: ghtonl, ghtons, and
+// so on. These functions do exactly the same as their standard variants,
+// but don't require including the dangerous netinet/in.h.
+//
+// Buffer routines will copy to and from buffers without causing
+// a bus error when the architecture requires different byte alignments.
+#ifndef S2_UTIL_ENDIAN_ENDIAN_H_
+#define S2_UTIL_ENDIAN_ENDIAN_H_
+
+#include <cassert>
+#include <type_traits>
+
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+#include "s2/base/port.h"
+#include "s2/third_party/absl/base/casts.h"
+#include "s2/third_party/absl/base/port.h"
+#include "s2/third_party/absl/numeric/int128.h"
+
+// Use compiler byte-swapping intrinsics if they are available. 32-bit
+// and 64-bit versions are available in Clang and GCC as of GCC 4.3.0.
+// The 16-bit version is available in Clang and GCC only as of GCC 4.8.0.
+// For simplicity, we enable them all only for GCC 4.8.0 or later.
+#if defined(__clang__) || \
+ (defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || \
+ __GNUC__ >= 5))
+
+inline uint64 gbswap_64(uint64 host_int) {
+ return __builtin_bswap64(host_int);
+}
+inline uint32 gbswap_32(uint32 host_int) {
+ return __builtin_bswap32(host_int);
+}
+inline uint16 gbswap_16(uint16 host_int) {
+ return __builtin_bswap16(host_int);
+}
+
+#else
+
+inline uint64 gbswap_64(uint64 host_int) {
+#if defined(__GNUC__) && defined(__x86_64__) && \
+ !(defined(__APPLE__) && defined(__MACH__))
+ // Adapted from /usr/include/byteswap.h. Not available on Mac.
+ if (__builtin_constant_p(host_int)) {
+ return __bswap_constant_64(host_int);
+ } else {
+ uint64 result;
+ __asm__("bswap %0" : "=r" (result) : "0" (host_int));
+ return result;
+ }
+#elif defined(bswap_64)
+ return bswap_64(host_int);
+#else
+ return static_cast<uint64>(bswap_32(static_cast<uint32>(host_int >> 32))) |
+ (static_cast<uint64>(bswap_32(static_cast<uint32>(host_int))) << 32);
+#endif // bswap_64
+}
+inline uint32 gbswap_32(uint32 host_int) {
+ return bswap_32(host_int);
+}
+inline uint16 gbswap_16(uint16 host_int) {
+ return bswap_16(host_int);
+}
+
+#endif // intrinics available
+
+inline absl::uint128 gbswap_128(absl::uint128 host_int) {
+ return absl::MakeUint128(gbswap_64(absl::Uint128Low64(host_int)),
+ gbswap_64(absl::Uint128High64(host_int)));
+}
+
+#ifdef IS_LITTLE_ENDIAN
+
+// Definitions for ntohl etc. that don't require us to include
+// netinet/in.h. We wrap gbswap_32 and gbswap_16 in functions rather
+// than just #defining them because in debug mode, gcc doesn't
+// correctly handle the (rather involved) definitions of bswap_32.
+// gcc guarantees that inline functions are as fast as macros, so
+// this isn't a performance hit.
+inline uint16 ghtons(uint16 x) { return gbswap_16(x); }
+inline uint32 ghtonl(uint32 x) { return gbswap_32(x); }
+inline uint64 ghtonll(uint64 x) { return gbswap_64(x); }
+
+#elif defined IS_BIG_ENDIAN
+
+// These definitions are simpler on big-endian machines
+// These are functions instead of macros to avoid self-assignment warnings
+// on calls such as "i = ghtnol(i);". This also provides type checking.
+inline uint16 ghtons(uint16 x) { return x; }
+inline uint32 ghtonl(uint32 x) { return x; }
+inline uint64 ghtonll(uint64 x) { return x; }
+
+#else
+#error "Unsupported bytesex: Either IS_BIG_ENDIAN or IS_LITTLE_ENDIAN must be defined" // NOLINT
+#endif // bytesex
+
+#ifndef htonll
+// With the rise of 64-bit, some systems are beginning to define this.
+#define htonll(x) ghtonll(x)
+#endif // htonll
+
+// ntoh* and hton* are the same thing for any size and bytesex,
+// since the function is an involution, i.e., its own inverse.
+inline uint16 gntohs(uint16 x) { return ghtons(x); }
+inline uint32 gntohl(uint32 x) { return ghtonl(x); }
+inline uint64 gntohll(uint64 x) { return ghtonll(x); }
+
+#ifndef ntohll
+#define ntohll(x) htonll(x)
+#endif // ntohll
+
+// We provide unified FromHost and ToHost APIs for all integral types and float,
+// double types. If variable v's type is known to be one of these types, the
+// client can simply call the following function without worrying about its
+// return type.
+// LittleEndian::FromHost(v), or BigEndian::FromHost(v)
+// LittleEndian::ToHost(v), or BigEndian::ToHost(v)
+// This unified FromHost and ToHost APIs are useful inside a template when the
+// type of v is a template parameter.
+//
+// In order to unify all "IntType FromHostxx(ValueType)" and "IntType
+// ToHostxx(ValueType)" APIs, we use the following trait class to automatically
+// find the corresponding IntType given a ValueType, where IntType is an
+// unsigned integer type with the same size of ValueType. The supported
+// ValueTypes are uint8, uint16, uint32, uint64, int8, int16, int32, int64,
+// bool, float, double.
+//
+// template <class ValueType>
+// struct tofromhost_value_type_traits {
+// typedef ValueType value_type;
+// typedef IntType int_type;
+// }
+//
+// We don't provide the default implementation for this trait struct.
+// So that if ValueType is not supported by the FromHost and ToHost APIs, it
+// will give a compile time error.
+template <class ValueType>
+struct tofromhost_value_type_traits;
+
+// General byte order converter class template. It provides a common
+// implementation for LittleEndian::FromHost(ValueType),
+// BigEndian::FromHost(ValueType), LittleEndian::ToHost(ValueType), and
+// BigEndian::ToHost(ValueType).
+template <class EndianClass, typename ValueType>
+class GeneralFormatConverter {
+ public:
+ static typename tofromhost_value_type_traits<ValueType>::int_type FromHost(
+ ValueType v);
+ static typename tofromhost_value_type_traits<ValueType>::int_type ToHost(
+ ValueType v);
+};
+
+// Utilities to convert numbers between the current hosts's native byte
+// order and little-endian byte order
+//
+// Load/Store methods are alignment safe
+class LittleEndian {
+ public:
+ // Conversion functions.
+#ifdef IS_LITTLE_ENDIAN
+
+ static uint16 FromHost16(uint16 x) { return x; }
+ static uint16 ToHost16(uint16 x) { return x; }
+
+ static uint32 FromHost32(uint32 x) { return x; }
+ static uint32 ToHost32(uint32 x) { return x; }
+
+ static uint64 FromHost64(uint64 x) { return x; }
+ static uint64 ToHost64(uint64 x) { return x; }
+
+ static absl::uint128 FromHost128(absl::uint128 x) { return x; }
+ static absl::uint128 ToHost128(absl::uint128 x) { return x; }
+
+ static constexpr bool IsLittleEndian() { return true; }
+
+#elif defined IS_BIG_ENDIAN
+
+ static uint16 FromHost16(uint16 x) { return gbswap_16(x); }
+ static uint16 ToHost16(uint16 x) { return gbswap_16(x); }
+
+ static uint32 FromHost32(uint32 x) { return gbswap_32(x); }
+ static uint32 ToHost32(uint32 x) { return gbswap_32(x); }
+
+ static uint64 FromHost64(uint64 x) { return gbswap_64(x); }
+ static uint64 ToHost64(uint64 x) { return gbswap_64(x); }
+
+ static absl::uint128 FromHost128(absl::uint128 x) { return gbswap_128(x); }
+ static absl::uint128 ToHost128(absl::uint128 x) { return gbswap_128(x); }
+
+ static constexpr bool IsLittleEndian() { return false; }
+
+#endif /* ENDIAN */
+
+ // Unified LittleEndian::FromHost(ValueType v) API.
+ template <class ValueType>
+ static typename tofromhost_value_type_traits<ValueType>::int_type FromHost(
+ ValueType v) {
+ return GeneralFormatConverter<LittleEndian, ValueType>::FromHost(v);
+ }
+
+ // Unified LittleEndian::ToHost(ValueType v) API.
+ template <class ValueType>
+ static typename tofromhost_value_type_traits<ValueType>::value_type ToHost(
+ ValueType v) {
+ return GeneralFormatConverter<LittleEndian, ValueType>::ToHost(v);
+ }
+
+ // Functions to do unaligned loads and stores in little-endian order.
+ static uint16 Load16(const void *p) {
+ return ToHost16(UNALIGNED_LOAD16(p));
+ }
+
+ static void Store16(void *p, uint16 v) {
+ UNALIGNED_STORE16(p, FromHost16(v));
+ }
+
+ static uint32 Load24(const void* p) {
+#ifdef IS_LITTLE_ENDIAN
+ uint32 result = 0;
+ memcpy(&result, p, 3);
+ return result;
+#else
+ const uint8* data = reinterpret_cast<const uint8*>(p);
+ return Load16(data) + (data[2] << 16);
+#endif
+ }
+
+ static void Store24(void* p, uint32 v) {
+#ifdef IS_LITTLE_ENDIAN
+ memcpy(p, &v, 3);
+#else
+ uint8* data = reinterpret_cast<uint8*>(p);
+ data[0] = v & 0xFF;
+ data[1] = (v >> 8) & 0xFF;
+ data[2] = (v >> 16) & 0xFF;
+#endif
+ }
+
+ static uint32 Load32(const void *p) {
+ return ToHost32(UNALIGNED_LOAD32(p));
+ }
+
+ static void Store32(void *p, uint32 v) {
+ UNALIGNED_STORE32(p, FromHost32(v));
+ }
+
+ static uint64 Load64(const void *p) {
+ return ToHost64(UNALIGNED_LOAD64(p));
+ }
+
+ // Build a uint64 from 1-8 bytes.
+ // 8 * len least significant bits are loaded from the memory with
+ // LittleEndian order. The 64 - 8 * len most significant bits are
+ // set all to 0.
+ // In latex-friendly words, this function returns:
+ // $\sum_{i=0}^{len-1} p[i] 256^{i}$, where p[i] is unsigned.
+ //
+ // This function is equivalent to:
+ // uint64 val = 0;
+ // memcpy(&val, p, len);
+ // return ToHost64(val);
+ // TODO(jyrki): write a small benchmark and benchmark the speed
+ // of a memcpy based approach.
+ //
+ // For speed reasons this function does not work for len == 0.
+ // The caller needs to guarantee that 1 <= len <= 8.
+ static uint64 Load64VariableLength(const void * const p, int len) {
+ assert(len >= 1 && len <= 8);
+ const char * const buf = static_cast<const char * const>(p);
+ uint64 val = 0;
+ --len;
+ do {
+ val = (val << 8) | buf[len];
+ // (--len >= 0) is about 10 % faster than (len--) in some benchmarks.
+ } while (--len >= 0);
+ // No ToHost64(...) needed. The bytes are accessed in little-endian manner
+ // on every architecture.
+ return val;
+ }
+
+ static void Store64(void *p, uint64 v) {
+ UNALIGNED_STORE64(p, FromHost64(v));
+ }
+
+ static absl::uint128 Load128(const void* p) {
+ return absl::MakeUint128(
+ ToHost64(UNALIGNED_LOAD64(reinterpret_cast<const uint64*>(p) + 1)),
+ ToHost64(UNALIGNED_LOAD64(p)));
+ }
+
+ static void Store128(void* p, const absl::uint128 v) {
+ UNALIGNED_STORE64(p, FromHost64(absl::Uint128Low64(v)));
+ UNALIGNED_STORE64(reinterpret_cast<uint64*>(p) + 1,
+ FromHost64(absl::Uint128High64(v)));
+ }
+
+ // Build a uint128 from 1-16 bytes.
+ // 8 * len least significant bits are loaded from the memory with
+ // LittleEndian order. The 128 - 8 * len most significant bits are
+ // set all to 0.
+ static absl::uint128 Load128VariableLength(const void* p, int len) {
+ if (len <= 8) {
+ return absl::uint128(Load64VariableLength(p, len));
+ } else {
+ return absl::MakeUint128(
+ Load64VariableLength(static_cast<const char*>(p) + 8, len - 8),
+ Load64(p));
+ }
+ }
+
+ // Load & Store in machine's word size.
+ static uword_t LoadUnsignedWord(const void *p) {
+ if (sizeof(uword_t) == 8)
+ return Load64(p);
+ else
+ return Load32(p);
+ }
+
+ static void StoreUnsignedWord(void *p, uword_t v) {
+ if (sizeof(v) == 8)
+ Store64(p, v);
+ else
+ Store32(p, v);
+ }
+
+ // Unified LittleEndian::Load/Store<T> API.
+
+ // Returns the T value encoded by the leading bytes of 'p', interpreted
+ // according to the format specified below. 'p' has no alignment restrictions.
+ //
+ // Type Format
+ // ---------------- -------------------------------------------------------
+ // uint{8,16,32,64} Little-endian binary representation.
+ // int{8,16,32,64} Little-endian twos-complement binary representation.
+ // float,double Little-endian IEEE-754 format.
+ // char The raw byte.
+ // bool A byte. 0 maps to false; all other values map to true.
+ template<typename T>
+ static T Load(const char* p);
+
+ // Encodes 'value' in the format corresponding to T. Supported types are
+ // described in Load<T>(). 'p' has no alignment restrictions. In-place Store
+ // is safe (that is, it is safe to call
+ // Store(x, reinterpret_cast<char*>(&x))).
+ template<typename T>
+ static void Store(T value, char* p);
+};
+
+// Utilities to convert numbers between the current hosts's native byte
+// order and big-endian byte order (same as network byte order)
+//
+// Load/Store methods are alignment safe
+class BigEndian {
+ public:
+#ifdef IS_LITTLE_ENDIAN
+
+ static uint16 FromHost16(uint16 x) { return gbswap_16(x); }
+ static uint16 ToHost16(uint16 x) { return gbswap_16(x); }
+
+ static uint32 FromHost32(uint32 x) { return gbswap_32(x); }
+ static uint32 ToHost32(uint32 x) { return gbswap_32(x); }
+
+ static uint64 FromHost64(uint64 x) { return gbswap_64(x); }
+ static uint64 ToHost64(uint64 x) { return gbswap_64(x); }
+
+ static absl::uint128 FromHost128(absl::uint128 x) { return gbswap_128(x); }
+ static absl::uint128 ToHost128(absl::uint128 x) { return gbswap_128(x); }
+
+ static constexpr bool IsLittleEndian() { return true; }
+
+#elif defined IS_BIG_ENDIAN
+
+ static uint16 FromHost16(uint16 x) { return x; }
+ static uint16 ToHost16(uint16 x) { return x; }
+
+ static uint32 FromHost32(uint32 x) { return x; }
+ static uint32 ToHost32(uint32 x) { return x; }
+
+ static uint64 FromHost64(uint64 x) { return x; }
+ static uint64 ToHost64(uint64 x) { return x; }
+
+ static absl::uint128 FromHost128(absl::uint128 x) { return x; }
+ static absl::uint128 ToHost128(absl::uint128 x) { return x; }
+
+ static constexpr bool IsLittleEndian() { return false; }
+
+#endif /* ENDIAN */
+
+ // Unified BigEndian::FromHost(ValueType v) API.
+ template <class ValueType>
+ static typename tofromhost_value_type_traits<ValueType>::int_type FromHost(
+ ValueType v) {
+ return GeneralFormatConverter<BigEndian, ValueType>::FromHost(v);
+ }
+
+ // Unified BigEndian::ToHost(ValueType v) API.
+ template <class ValueType>
+ static typename tofromhost_value_type_traits<ValueType>::value_type ToHost(
+ ValueType v) {
+ return GeneralFormatConverter<BigEndian, ValueType>::ToHost(v);
+ }
+
+ // Functions to do unaligned loads and stores in big-endian order.
+ static uint16 Load16(const void *p) {
+ return ToHost16(UNALIGNED_LOAD16(p));
+ }
+
+ static void Store16(void *p, uint16 v) {
+ UNALIGNED_STORE16(p, FromHost16(v));
+ }
+
+ static uint32 Load24(const void* p) {
+ const uint8* data = reinterpret_cast<const uint8*>(p);
+ return (data[0] << 16) + Load16(data + 1);
+ }
+
+ static void Store24(void* p, uint32 v) {
+ uint8* data = reinterpret_cast<uint8*>(p);
+ Store16(data + 1, static_cast<uint16>(v));
+ *data = static_cast<uint8>(v >> 16);
+ }
+
+ static uint32 Load32(const void *p) {
+ return ToHost32(UNALIGNED_LOAD32(p));
+ }
+
+ static void Store32(void *p, uint32 v) {
+ UNALIGNED_STORE32(p, FromHost32(v));
+ }
+
+ static uint64 Load64(const void *p) {
+ return ToHost64(UNALIGNED_LOAD64(p));
+ }
+
+ // Semantically build a uint64 from 1-8 bytes.
+ // 8 * len least significant bits are loaded from the memory with
+ // BigEndian order. The 64 - 8 * len most significant bits are
+ // set all to 0.
+ // In latex-friendly words, this function returns:
+ // $\sum_{i=0}^{len-1} p[i] 256^{i}$, where p[i] is unsigned.
+ //
+ // This function is equivalent to:
+ // uint64 val = 0;
+ // memcpy(&val, p, len);
+ // return ToHost64(val);
+ // TODO(jyrki): write a small benchmark and benchmark the speed
+ // of a memcpy based approach.
+ //
+ // For speed reasons this function does not work for len == 0.
+ // The caller needs to guarantee that 1 <= len <= 8.
+
+ static uint64 Load64VariableLength(const void * const p, int len) {
+ // uint64 val = LittleEndian::Load64VariableLength(p, len);
+ // return Load64(&val) >> (8*(8-len));
+ assert(len >= 1 && len <= 8);
+ const char* buf = static_cast<const char * const>(p);
+ uint64 val = 0;
+ do {
+ val = (val << 8) | *buf;
+ ++buf;
+ } while (--len > 0);
+ return val;
+ }
+
+ static void Store64(void *p, uint64 v) {
+ UNALIGNED_STORE64(p, FromHost64(v));
+ }
+
+ static absl::uint128 Load128(const void* p) {
+ return absl::MakeUint128(
+ ToHost64(UNALIGNED_LOAD64(p)),
+ ToHost64(UNALIGNED_LOAD64(reinterpret_cast<const uint64*>(p) + 1)));
+ }
+
+ static void Store128(void* p, const absl::uint128 v) {
+ UNALIGNED_STORE64(p, FromHost64(absl::Uint128High64(v)));
+ UNALIGNED_STORE64(reinterpret_cast<uint64*>(p) + 1,
+ FromHost64(absl::Uint128Low64(v)));
+ }
+
+ // Build a uint128 from 1-16 bytes.
+ // 8 * len least significant bits are loaded from the memory with
+ // BigEndian order. The 128 - 8 * len most significant bits are
+ // set all to 0.
+ static absl::uint128 Load128VariableLength(const void* p, int len) {
+ if (len <= 8) {
+ return absl::uint128(
+ Load64VariableLength(static_cast<const char*>(p), len));
+ } else if (len < 16) {
+ return absl::MakeUint128(Load64VariableLength(p, len - 8),
+ Load64(static_cast<const char*>(p) + len - 8));
+ } else {
+ return absl::MakeUint128(Load64(static_cast<const char*>(p)),
+ Load64(static_cast<const char*>(p) + 8));
+ }
+ }
+
+ // Load & Store in machine's word size.
+ static uword_t LoadUnsignedWord(const void *p) {
+ if (sizeof(uword_t) == 8)
+ return Load64(p);
+ else
+ return Load32(p);
+ }
+
+ static void StoreUnsignedWord(void *p, uword_t v) {
+ if (sizeof(uword_t) == 8)
+ Store64(p, v);
+ else
+ Store32(p, v);
+ }
+
+ // Unified BigEndian::Load/Store<T> API.
+
+ // Returns the T value encoded by the leading bytes of 'p', interpreted
+ // according to the format specified below. 'p' has no alignment restrictions.
+ //
+ // Type Format
+ // ---------------- -------------------------------------------------------
+ // uint{8,16,32,64} Big-endian binary representation.
+ // int{8,16,32,64} Big-endian twos-complement binary representation.
+ // float,double Big-endian IEEE-754 format.
+ // char The raw byte.
+ // bool A byte. 0 maps to false; all other values map to true.
+ template<typename T>
+ static T Load(const char* p);
+
+ // Encodes 'value' in the format corresponding to T. Supported types are
+ // described in Load<T>(). 'p' has no alignment restrictions. In-place Store
+ // is safe (that is, it is safe to call
+ // Store(x, reinterpret_cast<char*>(&x))).
+ template<typename T>
+ static void Store(T value, char* p);
+}; // BigEndian
+
+// Network byte order is big-endian
+typedef BigEndian NetworkByteOrder;
+
+//////////////////////////////////////////////////////////////////////
+// Implementation details: Clients can stop reading here.
+//
+// Define ValueType->IntType mapping for the unified
+// "IntType FromHost(ValueType)" API. The mapping is implemented via
+// tofromhost_value_type_traits trait struct. Every legal ValueType has its own
+// specialization. There is no default body for this trait struct, so that
+// any type that is not supported by the unified FromHost API
+// will trigger a compile time error.
+#define FROMHOST_TYPE_MAP(ITYPE, VTYPE) \
+ template <> \
+ struct tofromhost_value_type_traits<VTYPE> { \
+ typedef VTYPE value_type; \
+ typedef ITYPE int_type; \
+ };
+
+// dd: the extra semi-colons here made it harder to track down other errors with
+// -Wpedantic (because the semi-colon is already defined in the marcro)
+FROMHOST_TYPE_MAP(uint8, uint8)
+FROMHOST_TYPE_MAP(uint8, int8)
+FROMHOST_TYPE_MAP(uint16, uint16)
+FROMHOST_TYPE_MAP(uint16, int16)
+FROMHOST_TYPE_MAP(uint32, uint32)
+FROMHOST_TYPE_MAP(uint32, int32)
+FROMHOST_TYPE_MAP(uint64, uint64)
+FROMHOST_TYPE_MAP(uint64, int64)
+FROMHOST_TYPE_MAP(uint32, float)
+FROMHOST_TYPE_MAP(uint64, double)
+FROMHOST_TYPE_MAP(uint8, bool)
+FROMHOST_TYPE_MAP(absl::uint128, absl::uint128)
+#undef FROMHOST_TYPE_MAP
+
+// Default implementation for the unified FromHost(ValueType) API, which
+// handles all integral types (ValueType is one of uint8, int8, uint16, int16,
+// uint32, int32, uint64, int64). The compiler will remove the switch case
+// branches and unnecessary static_cast, when the template is expanded.
+template <class EndianClass, typename ValueType>
+typename tofromhost_value_type_traits<ValueType>::int_type
+GeneralFormatConverter<EndianClass, ValueType>::FromHost(ValueType v) {
+ switch (sizeof(ValueType)) {
+ case 1:
+ return static_cast<uint8>(v);
+ break;
+ case 2:
+ return EndianClass::FromHost16(static_cast<uint16>(v));
+ break;
+ case 4:
+ return EndianClass::FromHost32(static_cast<uint32>(v));
+ break;
+ case 8:
+ return EndianClass::FromHost64(static_cast<uint64>(v));
+ break;
+ default:
+ S2_LOG(FATAL) << "Unexpected value size: " << sizeof(ValueType);
+ }
+}
+
+// Default implementation for the unified ToHost(ValueType) API, which handles
+// all integral types (ValueType is one of uint8, int8, uint16, int16, uint32,
+// int32, uint64, int64). The compiler will remove the switch case branches and
+// unnecessary static_cast, when the template is expanded.
+template <class EndianClass, typename ValueType>
+typename tofromhost_value_type_traits<ValueType>::int_type
+GeneralFormatConverter<EndianClass, ValueType>::ToHost(ValueType v) {
+ switch (sizeof(ValueType)) {
+ case 1:
+ return static_cast<uint8>(v);
+ break;
+ case 2:
+ return EndianClass::ToHost16(static_cast<uint16>(v));
+ break;
+ case 4:
+ return EndianClass::ToHost32(static_cast<uint32>(v));
+ break;
+ case 8:
+ return EndianClass::ToHost64(static_cast<uint64>(v));
+ break;
+ default:
+ S2_LOG(FATAL) << "Unexpected value size: " << sizeof(ValueType);
+ }
+}
+
+// Specialization of the unified FromHost(ValueType) API, which handles
+// float types (ValueType is float).
+template <class EndianClass>
+class GeneralFormatConverter<EndianClass, float> {
+ public:
+ static typename tofromhost_value_type_traits<float>::int_type FromHost(
+ float v) {
+ return EndianClass::FromHost32(absl::bit_cast<uint32>(v));
+ }
+ static typename tofromhost_value_type_traits<float>::int_type ToHost(
+ float v) {
+ return absl::bit_cast<float>(
+ EndianClass::ToHost32(absl::bit_cast<uint32>(v)));
+ }
+};
+
+// Specialization of the unified FromHost(ValueType) API, which handles
+// double types (ValueType is double).
+template <class EndianClass>
+class GeneralFormatConverter<EndianClass, double> {
+ public:
+ static typename tofromhost_value_type_traits<double>::int_type FromHost(
+ double v) {
+ return EndianClass::FromHost64(absl::bit_cast<uint64>(v));
+ }
+ static typename tofromhost_value_type_traits<double>::int_type ToHost(
+ double v) {
+ return absl::bit_cast<double>(
+ EndianClass::ToHost64(absl::bit_cast<uint64>(v)));
+ }
+};
+
+// Specialization of the unified FromHost(ValueType) API, which handles
+// uint128 types (ValueType is uint128).
+template <class EndianClass>
+class GeneralFormatConverter<EndianClass, absl::uint128> {
+ public:
+ static typename tofromhost_value_type_traits<absl::uint128>::int_type
+ FromHost(absl::uint128 v) {
+ return EndianClass::FromHost128(v);
+ }
+ static typename tofromhost_value_type_traits<absl::uint128>::int_type ToHost(
+ absl::uint128 v) {
+ return EndianClass::ToHost128(v);
+ }
+};
+
+namespace endian_internal {
+// Integer helper methods for the unified Load/Store APIs.
+
+// Which branch of the 'case' to use is decided at compile time, so despite the
+// apparent size of this function, it compiles into efficient code.
+template<typename EndianClass, typename T>
+inline T LoadInteger(const char* p) {
+ static_assert(sizeof(T) <= 8 && std::is_integral<T>::value,
+ "T needs to be an integral type with size <= 8.");
+ switch (sizeof(T)) {
+ case 1: return *reinterpret_cast<const T*>(p);
+ case 2: return EndianClass::ToHost16(UNALIGNED_LOAD16(p));
+ case 4: return EndianClass::ToHost32(UNALIGNED_LOAD32(p));
+ case 8: return EndianClass::ToHost64(UNALIGNED_LOAD64(p));
+ default: {
+ S2_LOG(FATAL) << "Not reached!";
+ return 0;
+ }
+ }
+}
+
+// Which branch of the 'case' to use is decided at compile time, so despite the
+// apparent size of this function, it compiles into efficient code.
+template<typename EndianClass, typename T>
+inline void StoreInteger(T value, char* p) {
+ static_assert(sizeof(T) <= 8 && std::is_integral<T>::value,
+ "T needs to be an integral type with size <= 8.");
+ switch (sizeof(T)) {
+ case 1: *reinterpret_cast<T*>(p) = value; break;
+ case 2: UNALIGNED_STORE16(p, EndianClass::FromHost16(value)); break;
+ case 4: UNALIGNED_STORE32(p, EndianClass::FromHost32(value)); break;
+ case 8: UNALIGNED_STORE64(p, EndianClass::FromHost64(value)); break;
+ default: {
+ S2_LOG(FATAL) << "Not reached!";
+ }
+ }
+}
+
+// Floating point helper methods for the unified Load/Store APIs.
+
+template<typename EndianClass>
+inline float LoadFloat(const char* p) {
+ return absl::bit_cast<float>(EndianClass::ToHost32(UNALIGNED_LOAD32(p)));
+}
+
+template<typename EndianClass>
+inline void StoreFloat(float value, char* p) {
+ UNALIGNED_STORE32(p, EndianClass::FromHost32(absl::bit_cast<uint32>(value)));
+}
+
+template<typename EndianClass>
+inline double LoadDouble(const char* p) {
+ return absl::bit_cast<double>(EndianClass::ToHost64(UNALIGNED_LOAD64(p)));
+}
+
+template<typename EndianClass>
+inline void StoreDouble(double value, char* p) {
+ UNALIGNED_STORE64(p, EndianClass::FromHost64(absl::bit_cast<uint64>(value)));
+}
+
+} // namespace endian_internal
+
+// Load/Store for integral values.
+
+template<typename T>
+inline T LittleEndian::Load(const char* p) {
+ return endian_internal::LoadInteger<LittleEndian, T>(p);
+}
+
+template<typename T>
+inline void LittleEndian::Store(T value, char* p) {
+ endian_internal::StoreInteger<LittleEndian, T>(value, p);
+}
+
+template<typename T>
+inline T BigEndian::Load(const char* p) {
+ return endian_internal::LoadInteger<BigEndian, T>(p);
+}
+
+template<typename T>
+inline void BigEndian::Store(T value, char* p) {
+ endian_internal::StoreInteger<BigEndian, T>(value, p);
+}
+
+// Load/Store for bool. Sanitizes bool on the way in for safety.
+
+template<>
+inline bool LittleEndian::Load<bool>(const char* p) {
+ static_assert(sizeof(bool) == 1, "Unexpected sizeof(bool)");
+ return *p != 0;
+}
+
+template<>
+inline void LittleEndian::Store<bool>(bool value, char* p) {
+ static_assert(sizeof(bool) == 1, "Unexpected sizeof(bool)");
+ *p = value ? 1 : 0;
+}
+
+template<>
+inline bool BigEndian::Load<bool>(const char* p) {
+ static_assert(sizeof(bool) == 1, "Unexpected sizeof(bool)");
+ return *p != 0;
+}
+
+template<>
+inline void BigEndian::Store<bool>(bool value, char* p) {
+ static_assert(sizeof(bool) == 1, "Unexpected sizeof(bool)");
+ *p = value ? 1 : 0;
+}
+
+// Load/Store for float.
+
+template<>
+inline float LittleEndian::Load<float>(const char* p) {
+ return endian_internal::LoadFloat<LittleEndian>(p);
+}
+
+template<>
+inline void LittleEndian::Store<float>(float value, char* p) {
+ endian_internal::StoreFloat<LittleEndian>(value, p);
+}
+
+template<>
+inline float BigEndian::Load<float>(const char* p) {
+ return endian_internal::LoadFloat<BigEndian>(p);
+}
+
+template<>
+inline void BigEndian::Store<float>(float value, char* p) {
+ endian_internal::StoreFloat<BigEndian>(value, p);
+}
+
+// Load/Store for double.
+
+template<>
+inline double LittleEndian::Load<double>(const char* p) {
+ return endian_internal::LoadDouble<LittleEndian>(p);
+}
+
+template<>
+inline void LittleEndian::Store<double>(double value, char* p) {
+ endian_internal::StoreDouble<LittleEndian>(value, p);
+}
+
+template<>
+inline double BigEndian::Load<double>(const char* p) {
+ return endian_internal::LoadDouble<BigEndian>(p);
+}
+
+template<>
+inline void BigEndian::Store<double>(double value, char* p) {
+ endian_internal::StoreDouble<BigEndian>(value, p);
+}
+
+// Load/Store for uint128.
+
+template <>
+inline absl::uint128 LittleEndian::Load<absl::uint128>(const char* p) {
+ return LittleEndian::Load128(p);
+}
+
+template <>
+inline void LittleEndian::Store<absl::uint128>(absl::uint128 value, char* p) {
+ LittleEndian::Store128(p, value);
+}
+
+template <>
+inline absl::uint128 BigEndian::Load<absl::uint128>(const char* p) {
+ return BigEndian::Load128(p);
+}
+
+template <>
+inline void BigEndian::Store<absl::uint128>(absl::uint128 value, char* p) {
+ BigEndian::Store128(p, value);
+}
+
+#endif // S2_UTIL_ENDIAN_ENDIAN_H_
--- /dev/null
+// Copyright 2007 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+//
+// A btree implementation of the STL set and map interfaces. A btree is smaller
+// and generally also faster than STL set/map (refer to the benchmarks below).
+// The red-black tree implementation of STL set/map has an overhead of 3
+// pointers (left, right and parent) plus the node color information for each
+// stored value. So a set<int32> consumes 40 bytes for each value stored in
+// 64-bit mode. This btree implementation stores multiple values on fixed
+// size nodes (usually 256 bytes) and doesn't store child pointers for leaf
+// nodes. The result is that a btree_set<int32> may use much less memory per
+// stored value. For the random insertion benchmark in btree_bench.cc, a
+// btree_set<int32> with node-size of 256 uses 5.1 bytes per stored value.
+//
+// The packing of multiple values on to each node of a btree has another effect
+// besides better space utilization: better cache locality due to fewer cache
+// lines being accessed. Better cache locality translates into faster
+// operations.
+//
+// CAVEATS
+//
+// Insertions and deletions on a btree can cause splitting, merging or
+// rebalancing of btree nodes. And even without these operations, insertions
+// and deletions on a btree will move values around within a node. In both
+// cases, the result is that insertions and deletions can invalidate iterators
+// pointing to values other than the one being inserted/deleted. Therefore, this
+// container does not provide pointer stability. This is notably different from
+// STL set/map which takes care to not invalidate iterators on insert/erase
+// except, of course, for iterators pointing to the value being erased. A
+// partial workaround when erasing is available: erase() returns an iterator
+// pointing to the item just after the one that was erased (or end() if none
+// exists).
+
+// PERFORMANCE
+//
+// See the latest benchmark results at:
+// https://paste.googleplex.com/5549632792821760
+//
+
+#ifndef S2_UTIL_GTL_BTREE_H_
+#define S2_UTIL_GTL_BTREE_H_
+
+#include <cstddef>
+#include <cstring>
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <new>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include "s2/base/integral_types.h"
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/third_party/absl/container/internal/compressed_tuple.h"
+#include "s2/third_party/absl/container/internal/container_memory.h"
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/third_party/absl/meta/type_traits.h"
+#include "s2/third_party/absl/strings/string_view.h"
+#include "s2/third_party/absl/utility/utility.h"
+#include "s2/util/gtl/layout.h"
+
+namespace gtl {
+
+// A helper type used to indicate that a key-compare-to functor has been
+// provided. A key-compare-to functor compares two arguments of type value_type
+// and returns 0 if they are equal, a negative integer when the first argument
+// should be first, and a positive integer otherwise. A user can specify a
+// key-compare-to functor by doing:
+//
+// struct MyStringComparer
+// : public gtl::btree_key_compare_to_tag {
+// int operator()(const string &a, const string &b) const {
+// return a.compare(b);
+// }
+// };
+//
+// Note that the return type is an int and not a bool. There is a
+// static_assert which enforces this return type.
+// TODO(user): get rid of this tag and just detect whether there is an operator
+struct btree_key_compare_to_tag {};
+
+// A helper class that indicates if the Compare parameter is derived from
+// btree_key_compare_to_tag.
+template <typename Compare>
+using btree_is_key_compare_to =
+ std::is_convertible<Compare, btree_key_compare_to_tag>;
+
+namespace internal_btree {
+// A helper class used to indicate if the comparator provided is transparent
+// and thus supports heterogeneous lookups. This is only used internally to
+// check if the Compare parameter has a valid is_transparent member.
+// A transparent comparator will see lookup keys with any type (lookup_type)
+// passed by the user to any of the lookup methods. The comparator then has a
+// chance to do the comparison without first converting the lookup key to a
+// key_type.
+//
+// For example, a comparator that is transparent may look like:
+//
+// struct MyStringComparer {
+// bool operator()(const string &a, const string &b) const {
+// return a < b;
+// }
+// bool operator()(const string &a, const char* b) const {
+// return strcmp(a.c_str(), b) < 0;
+// }
+// bool operator()(const char* a, const string& b) const {
+// return strcmp(a, b.c_str()) < 0;
+// }
+// using is_transparent = void;
+// };
+//
+// Note that we need to declare operator() for both combinations of key_type and
+// lookup_type. Also note that setting is_transparent to void is an arbitrary
+// decision; it can be std::true_type, int, or anything else, just as long as
+// the member is_transparent is defined to be something.
+template <typename, typename = void>
+struct is_comparator_transparent : std::false_type {};
+template <typename Compare>
+struct is_comparator_transparent<Compare,
+ absl::void_t<typename Compare::is_transparent>>
+ : std::true_type {};
+
+// A helper class to convert a boolean comparison into a three-way "compare-to"
+// comparison that returns a negative value to indicate less-than, zero to
+// indicate equality and a positive value to indicate greater-than. This helper
+// class is specialized for less<string>, greater<string>, less<string_view>,
+// and greater<string_view>. The
+// key_compare_to_adapter is provided so that btree users
+// automatically get the more efficient compare-to code when using common
+// google string types with common comparison functors.
+// TODO(user): see if we can extract this logic so that it can be used with
+template <typename Compare>
+struct key_compare_to_adapter {
+ using type = Compare;
+};
+
+template <>
+struct key_compare_to_adapter<std::less<std::string>> {
+ struct type : public btree_key_compare_to_tag {
+ type() = default;
+ explicit type(const std::less<std::string> &) {}
+ int operator()(const std::string &a, const std::string &b) const {
+ return a.compare(b);
+ }
+ };
+};
+
+template <>
+struct key_compare_to_adapter<std::greater<std::string>> {
+ struct type : public btree_key_compare_to_tag {
+ type() = default;
+ explicit type(const std::greater<std::string> &) {}
+ int operator()(const std::string &a, const std::string &b) const {
+ return b.compare(a);
+ }
+ };
+};
+
+template <>
+struct key_compare_to_adapter<std::less<absl::string_view>> {
+ struct type : public btree_key_compare_to_tag {
+ type() = default;
+ explicit type(const std::less<absl::string_view> &) {}
+ int operator()(const absl::string_view a, const absl::string_view b) const {
+ return a.compare(b);
+ }
+ };
+};
+
+template <>
+struct key_compare_to_adapter<std::greater<absl::string_view>> {
+ struct type : public btree_key_compare_to_tag {
+ type() = default;
+ explicit type(const std::greater<absl::string_view> &) {}
+ int operator()(const absl::string_view a, const absl::string_view b) const {
+ return b.compare(a);
+ }
+ };
+};
+
+
+// A helper function to do a boolean comparison of two keys given a boolean
+// or key-compare-to (three-way) comparator.
+template <typename K, typename LK, typename Compare>
+bool bool_compare_keys(const Compare &comp, const K &x, const LK &y) {
+ return btree_is_key_compare_to<Compare>::value ? comp(x, y) < 0 : comp(x, y);
+}
+
+// Detects a 'goog_btree_prefer_linear_node_search' member. This is
+// a protocol used as an opt-in or opt-out of linear search.
+//
+// For example, this would be useful for key types that wrap an integer
+// and define their own cheap operator<(). For example:
+//
+// class K {
+// public:
+// using goog_btree_prefer_linear_node_search = std::true_type;
+// ...
+// private:
+// friend bool operator<(K a, K b) { return a.k_ < b.k_; }
+// int k_;
+// };
+//
+// btree_map<K, V> m; // Uses linear search
+// assert((btree_map<K, V>::testonly_uses_linear_node_search()));
+//
+// If T has the preference tag, then it has a preference.
+// Btree will use the tag's truth value.
+template <typename T, typename = void>
+struct has_linear_node_search_preference : std::false_type {};
+template <typename T, typename = void>
+struct prefers_linear_node_search : std::false_type {};
+template <typename T>
+struct has_linear_node_search_preference<
+ T, absl::void_t<typename T::goog_btree_prefer_linear_node_search>>
+ : std::true_type {};
+template <typename T>
+struct prefers_linear_node_search<
+ T, absl::void_t<typename T::goog_btree_prefer_linear_node_search>>
+ : T::goog_btree_prefer_linear_node_search {};
+
+template <typename Key, typename Compare, typename Alloc, int TargetNodeSize,
+ int ValueSize, bool Multi>
+struct common_params {
+ // If Compare is derived from btree_key_compare_to_tag then use it as the
+ // key_compare type. Otherwise, use key_compare_to_adapter<> which will
+ // fall-back to Compare if we don't have an appropriate specialization.
+ using key_compare =
+ absl::conditional_t<btree_is_key_compare_to<Compare>::value, Compare,
+ typename key_compare_to_adapter<Compare>::type>;
+ // A type which indicates if we have a key-compare-to functor or a plain old
+ // key-compare functor.
+ using is_key_compare_to = btree_is_key_compare_to<key_compare>;
+
+ using allocator_type = Alloc;
+ using key_type = Key;
+ using size_type = std::make_signed<size_t>::type;
+ using difference_type = ptrdiff_t;
+
+ // True if this is a multiset or multimap.
+ using is_multi_container = std::integral_constant<bool, Multi>;
+
+ enum {
+ kTargetNodeSize = TargetNodeSize,
+
+ // Upper bound for the available space for values. This is largest for leaf
+ // nodes, which have overhead of at least a pointer + 4 bytes (for storing
+ // 3 field_types and an enum).
+ kNodeValueSpace =
+ TargetNodeSize - /*minimum overhead=*/(sizeof(void *) + 4),
+ };
+
+ // This is an integral type large enough to hold as many
+ // ValueSize-values as will fit a node of TargetNodeSize bytes.
+ using node_count_type =
+ absl::conditional_t<(kNodeValueSpace / ValueSize >
+ std::numeric_limits<uint8>::max()),
+ uint16, uint8>; // NOLINT
+ static_assert(kNodeValueSpace / ValueSize <=
+ std::numeric_limits<uint16>::max(),
+ "uint16 is not big enough for node_count_type.");
+};
+
+// A parameters structure for holding the type parameters for a btree_map.
+// Compare and Alloc should be nothrow copy-constructible.
+template <typename Key, typename Data, typename Compare, typename Alloc,
+ int TargetNodeSize, bool Multi>
+struct map_params : common_params<Key, Compare, Alloc, TargetNodeSize,
+ sizeof(std::pair<const Key, Data>), Multi> {
+ using mapped_type = Data;
+ // This type allows us to move keys when it is safe to do so. It is safe
+ // for maps in which value_type and mutable_value_type are layout compatible.
+ using slot_type = absl::container_internal::slot_type<Key, mapped_type>;
+ using value_type = typename slot_type::value_type;
+ using mutable_value_type = typename slot_type::mutable_value_type;
+ using pointer = value_type *;
+ using const_pointer = const value_type *;
+ using reference = value_type &;
+ using const_reference = const value_type &;
+
+ using key_compare = typename map_params::common_params::key_compare;
+ // Inherit from key_compare for empty base class optimization.
+ struct value_compare : private key_compare {
+ value_compare() = default;
+ explicit value_compare(const key_compare &cmp) : key_compare(cmp) {}
+
+ template <typename T, typename U>
+ absl::conditional_t<btree_is_key_compare_to<key_compare>::value, int, bool>
+ operator()(const T &left, const U &right) const {
+ return key_compare::operator()(left.first, right.first);
+ }
+ };
+
+ static const Key& key(const value_type &x) { return x.first; }
+ static const Key& key(const mutable_value_type &x) { return x.first; }
+};
+
+// A parameters structure for holding the type parameters for a btree_set.
+// Compare and Alloc should be nothrow copy-constructible.
+template <typename Key, typename Compare, typename Alloc, int TargetNodeSize,
+ bool Multi>
+struct set_params
+ : common_params<Key, Compare, Alloc, TargetNodeSize, sizeof(Key), Multi> {
+ using mapped_type = void;
+ using value_type = Key;
+ using mutable_value_type = Key;
+ // This type implements the necessary functions from the
+ // absl::container_internal::slot_type interface.
+ struct slot_type {
+ value_type value;
+
+ template <class... Args>
+ static void construct(Alloc *alloc, slot_type *slot, Args &&... args) {
+ absl::allocator_traits<Alloc>::construct(*alloc, &slot->value,
+ std::forward<Args>(args)...);
+ }
+
+ static void construct(Alloc *alloc, slot_type *slot, slot_type *other) {
+ absl::allocator_traits<Alloc>::construct(*alloc, &slot->value,
+ std::move(other->value));
+ }
+
+ static void destroy(Alloc *alloc, slot_type *slot) {
+ absl::allocator_traits<Alloc>::destroy(*alloc, &slot->value);
+ }
+
+ static void swap(Alloc * /*alloc*/, slot_type *a, slot_type *b) {
+ using std::swap;
+ swap(a->value, b->value);
+ }
+
+ static void move(Alloc * /*alloc*/, slot_type *src, slot_type *dest) {
+ dest->value = std::move(src->value);
+ }
+
+ static void move(Alloc *alloc, slot_type *first, slot_type *last,
+ slot_type *result) {
+ for (slot_type *src = first, *dest = result; src != last; ++src, ++dest)
+ move(alloc, src, dest);
+ }
+ };
+ using pointer = value_type *;
+ using const_pointer = const value_type *;
+ using reference = value_type &;
+ using const_reference = const value_type &;
+ using value_compare = typename set_params::common_params::key_compare;
+
+ static const Key& key(const value_type &x) { return x; }
+};
+
+// An adapter class that converts a lower-bound compare into an upper-bound
+// compare. Note: there is no need to make a version of this adapter specialized
+// for key-compare-to functors because the upper-bound (the first value greater
+// than the input) is never an exact match.
+template <typename Compare>
+struct upper_bound_adapter {
+ explicit upper_bound_adapter(const Compare &c) : comp(c) {}
+ template <typename K, typename LK>
+ bool operator()(const K &a, const LK &b) const {
+ // Returns true when a is not greater than b.
+ return !bool_compare_keys(comp, b, a);
+ }
+
+ private:
+ Compare comp;
+};
+
+// A node in the btree holding. The same node type is used for both internal
+// and leaf nodes in the btree, though the nodes are allocated in such a way
+// that the children array is only valid in internal nodes.
+template <typename Params>
+class btree_node {
+ using is_key_compare_to = typename Params::is_key_compare_to;
+ using is_multi_container = typename Params::is_multi_container;
+ using field_type = typename Params::node_count_type;
+ using allocator_type = typename Params::allocator_type;
+ using slot_type = typename Params::slot_type;
+
+ public:
+ using params_type = Params;
+ using key_type = typename Params::key_type;
+ using value_type = typename Params::value_type;
+ using mutable_value_type = typename Params::mutable_value_type;
+ using pointer = typename Params::pointer;
+ using const_pointer = typename Params::const_pointer;
+ using reference = typename Params::reference;
+ using const_reference = typename Params::const_reference;
+ using key_compare = typename Params::key_compare;
+ using size_type = typename Params::size_type;
+ using difference_type = typename Params::difference_type;
+
+ // Btree's choice of binary search or linear search is a customization
+ // point that can be configured via the key_compare and key_type.
+ // Btree decides whether to use linear node search as follows:
+ // - If the comparator expresses a preference, use that.
+ // - Otherwise, if the key expresses a preference, use that.
+ // - Otherwise, if the key is arithmetic and the comparator is std::less or
+ // std::greater, choose linear.
+ // - Otherwise, choose binary.
+ // See documentation for has_linear_node_search_preference and
+ // prefers_linear_node_search above.
+ // Might be wise to also configure linear search based on node-size.
+ using use_linear_search = absl::conditional_t<
+ has_linear_node_search_preference<key_compare>::value
+ ? prefers_linear_node_search<key_compare>::value
+ : has_linear_node_search_preference<key_type>::value
+ ? prefers_linear_node_search<key_type>::value
+ : std::is_arithmetic<key_type>::value &&
+ (std::is_same<std::less<key_type>, key_compare>::value ||
+ std::is_same<std::greater<key_type>,
+ key_compare>::value),
+ std::true_type, std::false_type>;
+
+ // This class is organized by gtl::Layout as if it had the following
+ // structure:
+ // // A pointer to the node's parent.
+ // btree_node *parent;
+ //
+ // // The position of the node in the node's parent.
+ // field_type position;
+ // // The index of the first populated value in `values`.
+ // // TODO(user): right now, `start` is always 0. Update insertion/merge
+ // // logic to allow for floating storage within nodes.
+ // field_type start;
+ // // The count of the number of populated values in the node.
+ // field_type count;
+ // // The maximum number of values the node can hold. This is an integer in
+ // // [1, kNodeValues] for root leaf nodes, kNodeValues for non-root leaf
+ // // nodes, and kInternalNodeMaxCount (as a sentinel value) for internal
+ // // nodes (even though there are still kNodeValues values in the node).
+ // // TODO(user): make max_count use only 4 bits and record log2(capacity)
+ // // to free extra bits for is_root, etc.
+ // field_type max_count;
+ //
+ // // The array of values. The capacity is `max_count` for leaf nodes and
+ // // kNodeValues for internal nodes. Only the values in
+ // // [start, start + count) have been initialized and are valid.
+ // slot_type values[max_count];
+ //
+ // // The array of child pointers. The keys in children[i] are all less
+ // // than key(i). The keys in children[i + 1] are all greater than key(i).
+ // // There are 0 children for leaf nodes and kNodeValues + 1 children for
+ // // internal nodes.
+ // btree_node *children[kNodeValues + 1];
+ //
+ // This class is never constructed or deleted. Instead, pointers to the layout
+ // above are allocated, cast to btree_node*, and de-allocated within the btree
+ // implementation.
+ btree_node() = delete;
+ ~btree_node() = delete;
+ btree_node(btree_node const &) = delete;
+ btree_node &operator=(btree_node const &) = delete;
+
+ private:
+ using layout_type = Layout<btree_node *, field_type, slot_type, btree_node *>;
+ constexpr static size_type SizeWithNValues(size_type n) {
+ return layout_type(/*parent*/ 1,
+ /*position, start, count, max_count*/ 4,
+ /*values*/ n,
+ /*children*/ 0)
+ .AllocSize();
+ }
+ // A lower bound for the overhead of fields other than values in a leaf node.
+ constexpr static size_type MinimumOverhead() {
+ return SizeWithNValues(1) - sizeof(value_type);
+ }
+
+ // Compute how many values we can fit onto a leaf node taking into account
+ // padding.
+ constexpr static size_type NodeTargetValues(const int begin, const int end) {
+ return begin == end ? begin
+ : SizeWithNValues((begin + end) / 2 + 1) >
+ params_type::kTargetNodeSize
+ ? NodeTargetValues(begin, (begin + end) / 2)
+ : NodeTargetValues((begin + end) / 2 + 1, end);
+ }
+
+ enum {
+ kTargetNodeSize = params_type::kTargetNodeSize,
+ kNodeTargetValues = NodeTargetValues(0, params_type::kTargetNodeSize),
+
+ // We need a minimum of 3 values per internal node in order to perform
+ // splitting (1 value for the two nodes involved in the split and 1 value
+ // propagated to the parent as the delimiter for the split).
+ kNodeValues = kNodeTargetValues >= 3 ? kNodeTargetValues : 3,
+
+ kExactMatch = 1 << 30,
+ kMatchMask = kExactMatch - 1,
+
+ // The node is internal (i.e. is not a leaf node) if and only if `max_count`
+ // has this value.
+ kInternalNodeMaxCount = 0,
+ };
+
+ // Leaves can have less than kNodeValues values.
+ constexpr static layout_type LeafLayout(const int max_values = kNodeValues) {
+ return layout_type(/*parent*/ 1,
+ /*position, start, count, max_count*/ 4,
+ /*values*/ max_values,
+ /*children*/ 0);
+ }
+ constexpr static layout_type InternalLayout() {
+ return layout_type(/*parent*/ 1,
+ /*position, start, count, max_count*/ 4,
+ /*values*/ kNodeValues,
+ /*children*/ kNodeValues + 1);
+ }
+ constexpr static size_type LeafSize(const int max_values = kNodeValues) {
+ return LeafLayout(max_values).AllocSize();
+ }
+ constexpr static size_type InternalSize() {
+ return InternalLayout().AllocSize();
+ }
+ constexpr static size_type Alignment() {
+ static_assert(LeafLayout(1).Alignment() == InternalLayout().Alignment(),
+ "Alignment of all nodes must be equal.");
+ return InternalLayout().Alignment();
+ }
+
+ // N is the index of the type in the Layout definition.
+ // ElementType<N> is the Nth type in the Layout definition.
+ template <size_type N>
+ inline typename layout_type::template ElementType<N> *GetField() {
+ // We assert that we don't read from values that aren't there.
+ assert(N < 3 || !leaf());
+ return InternalLayout().template Pointer<N>(reinterpret_cast<char *>(this));
+ }
+ template <size_type N>
+ inline const typename layout_type::template ElementType<N> *GetField() const {
+ assert(N < 3 || !leaf());
+ return InternalLayout().template Pointer<N>(
+ reinterpret_cast<const char *>(this));
+ }
+ void set_parent(btree_node *p) { *GetField<0>() = p; }
+ field_type &mutable_count() { return GetField<1>()[2]; }
+ slot_type *slot(int i) { return &GetField<2>()[i]; }
+ const slot_type *slot(int i) const { return &GetField<2>()[i]; }
+ void set_position(field_type v) { GetField<1>()[0] = v; }
+ void set_start(field_type v) { GetField<1>()[1] = v; }
+ void set_count(field_type v) { GetField<1>()[2] = v; }
+ // This method is only called by the node init methods.
+ void set_max_count(field_type v) { GetField<1>()[3] = v; }
+
+ public:
+ // Whether this is a leaf node or not. This value doesn't change after the
+ // node is created.
+ bool leaf() const { return GetField<1>()[3] != kInternalNodeMaxCount; }
+
+ // Getter for the position of this node in its parent.
+ field_type position() const { return GetField<1>()[0]; }
+
+ // Getter for the offset of the first value in the `values` array.
+ field_type start() const { return GetField<1>()[1]; }
+
+ // Getters for the number of values stored in this node.
+ field_type count() const { return GetField<1>()[2]; }
+ field_type max_count() const {
+ // Internal nodes have max_count==kInternalNodeMaxCount.
+ // Leaf nodes have max_count in [1, kNodeValues].
+ const field_type max_count = GetField<1>()[3];
+ return max_count == kInternalNodeMaxCount ? kNodeValues : max_count;
+ }
+
+ // Getter for the parent of this node.
+ btree_node *parent() const { return *GetField<0>(); }
+ // Getter for whether the node is the root of the tree. The parent of the
+ // root of the tree is the leftmost node in the tree which is guaranteed to
+ // be a leaf.
+ bool is_root() const { return parent()->leaf(); }
+ void make_root() {
+ assert(parent()->is_root());
+ set_parent(parent()->parent());
+ }
+
+ // Getters for the key/value at position i in the node.
+ const key_type &key(int i) const { return params_type::key(slot(i)->value); }
+ reference value(int i) { return slot(i)->value; }
+ const_reference value(int i) const { return slot(i)->value; }
+
+ // Getters/setter for the child at position i in the node.
+ btree_node *child(int i) const { return GetField<3>()[i]; }
+ btree_node *&mutable_child(int i) { return GetField<3>()[i]; }
+ void clear_child(int i) {
+ absl::container_internal::SanitizerPoisonObject(&mutable_child(i));
+ }
+ void set_child(int i, btree_node *c) {
+ absl::container_internal::SanitizerUnpoisonObject(&mutable_child(i));
+ mutable_child(i) = c;
+ c->set_position(i);
+ }
+ void init_child(int i, btree_node *c) {
+ set_child(i, c);
+ c->set_parent(this);
+ }
+
+ // Returns the position of the first value whose key is not less than k.
+ template <typename K>
+ int lower_bound(const K &k, const key_compare &comp) const {
+ return use_linear_search::value ? linear_search(k, comp)
+ : binary_search(k, comp);
+ }
+ // Returns the position of the first value whose key is greater than k.
+ template <typename K>
+ int upper_bound(const K &k, const key_compare &comp) const {
+ auto upper_compare = upper_bound_adapter<key_compare>(comp);
+ return use_linear_search::value ? linear_search(k, upper_compare)
+ : binary_search(k, upper_compare);
+ }
+
+ template <typename K, typename Compare>
+ int linear_search(const K &k, const Compare &comp) const {
+ return btree_is_key_compare_to<Compare>::value
+ ? linear_search_compare_to(k, 0, count(), comp)
+ : linear_search_plain_compare(k, 0, count(), comp);
+ }
+
+ template <typename K, typename Compare>
+ int binary_search(const K &k, const Compare &comp) const {
+ return btree_is_key_compare_to<Compare>::value
+ ? binary_search_compare_to(k, 0, count(), comp)
+ : binary_search_plain_compare(k, 0, count(), comp);
+ }
+
+ // Returns the position of the first value whose key is not less than k using
+ // linear search performed using plain compare.
+ template <typename K, typename Compare>
+ int linear_search_plain_compare(const K &k, int s, const int e,
+ const Compare &comp) const {
+ while (s < e) {
+ if (!comp(key(s), k)) {
+ break;
+ }
+ ++s;
+ }
+ return s;
+ }
+
+ // Returns the position of the first value whose key is not less than k using
+ // linear search performed using compare-to.
+ template <typename K, typename Compare>
+ int linear_search_compare_to(const K &k, int s, const int e,
+ const Compare &comp) const {
+ while (s < e) {
+ const int c = comp(key(s), k);
+ if (c == 0) {
+ return s | kExactMatch;
+ } else if (c > 0) {
+ break;
+ }
+ ++s;
+ }
+ return s;
+ }
+
+ // Returns the position of the first value whose key is not less than k using
+ // binary search performed using plain compare.
+ template <typename K, typename Compare>
+ int binary_search_plain_compare(const K &k, int s, int e,
+ const Compare &comp) const {
+ while (s != e) {
+ const int mid = (s + e) >> 1;
+ if (comp(key(mid), k)) {
+ s = mid + 1;
+ } else {
+ e = mid;
+ }
+ }
+ return s;
+ }
+
+ // Returns the position of the first value whose key is not less than k using
+ // binary search performed using compare-to.
+ template <typename K, typename CompareTo>
+ int binary_search_compare_to(
+ const K &k, int s, int e, const CompareTo &comp) const {
+ if (is_multi_container::value) {
+ int exact_match = 0;
+ while (s != e) {
+ const int mid = (s + e) >> 1;
+ const int c = comp(key(mid), k);
+ if (c < 0) {
+ s = mid + 1;
+ } else {
+ e = mid;
+ if (c == 0) {
+ // Need to return the first value whose key is not less than k,
+ // which requires continuing the binary search if this is a
+ // multi-container.
+ exact_match = kExactMatch;
+ }
+ }
+ }
+ return s | exact_match;
+ } else { // Not a multi-container.
+ while (s != e) {
+ const int mid = (s + e) >> 1;
+ const int c = comp(key(mid), k);
+ if (c < 0) {
+ s = mid + 1;
+ } else if (c > 0) {
+ e = mid;
+ } else {
+ return mid | kExactMatch;
+ }
+ }
+ return s;
+ }
+ }
+
+ // Emplaces a value at position i, shifting all existing values and
+ // children at positions >= i to the right by 1.
+ template <typename... Args>
+ void emplace_value(size_type i, allocator_type *alloc, Args &&... args);
+
+ // Removes the value at position i, shifting all existing values and children
+ // at positions > i to the left by 1.
+ void remove_value(int i, allocator_type *alloc);
+
+ // Rebalances a node with its right sibling.
+ void rebalance_right_to_left(int to_move, btree_node *right,
+ allocator_type *alloc);
+ void rebalance_left_to_right(int to_move, btree_node *right,
+ allocator_type *alloc);
+
+ // Splits a node, moving a portion of the node's values to its right sibling.
+ void split(int insert_position, btree_node *dest, allocator_type *alloc);
+
+ // Merges a node with its right sibling, moving all of the values and the
+ // delimiting key in the parent node onto itself.
+ void merge(btree_node *sibling, allocator_type *alloc);
+
+ // Swap the contents of "this" and "src".
+ void swap(btree_node *src, allocator_type *alloc);
+
+ // Node allocation/deletion routines.
+ static btree_node *init_leaf(btree_node *n, btree_node *parent,
+ int max_count) {
+ n->set_parent(parent);
+ n->set_position(0);
+ n->set_start(0);
+ n->set_count(0);
+ n->set_max_count(max_count);
+ absl::container_internal::SanitizerPoisonMemoryRegion(
+ n->slot(0), max_count * sizeof(slot_type));
+ return n;
+ }
+ static btree_node *init_internal(btree_node *n, btree_node *parent) {
+ init_leaf(n, parent, kNodeValues);
+ // Set `max_count` to a sentinel value to indicate that this node is
+ // internal.
+ n->set_max_count(kInternalNodeMaxCount);
+ absl::container_internal::SanitizerPoisonMemoryRegion(
+ &n->mutable_child(0), (kNodeValues + 1) * sizeof(btree_node *));
+ return n;
+ }
+ void destroy(allocator_type *alloc) {
+ for (int i = 0; i < count(); ++i) {
+ value_destroy(i, alloc);
+ }
+ }
+
+ public:
+ // Exposed only for tests.
+ static bool testonly_uses_linear_node_search() {
+ return use_linear_search::value;
+ }
+
+ private:
+ template <typename... Args>
+ void value_init(const size_type i, allocator_type *alloc, Args &&... args) {
+ absl::container_internal::SanitizerUnpoisonObject(slot(i));
+ slot_type::construct(alloc, slot(i), std::forward<Args>(args)...);
+ }
+ void value_destroy(const size_type i, allocator_type *alloc) {
+ slot_type::destroy(alloc, slot(i));
+ absl::container_internal::SanitizerPoisonObject(slot(i));
+ }
+
+ // Move n values starting at value i in this node into the values starting at
+ // value j in node x.
+ void uninitialized_move_n(const size_type n, const size_type i,
+ const size_type j, btree_node *x,
+ allocator_type *alloc) {
+ absl::container_internal::SanitizerUnpoisonMemoryRegion(
+ x->slot(j), n * sizeof(slot_type));
+ for (slot_type *src = slot(i), *end = src + n, *dest = x->slot(j);
+ src != end; ++src, ++dest) {
+ slot_type::construct(alloc, dest, src);
+ }
+ }
+
+ // Destroys a range of n values, starting at index i.
+ void value_destroy_n(const size_type i, const size_type n,
+ allocator_type *alloc) {
+ for (int j = 0; j < n; ++j) {
+ value_destroy(i + j, alloc);
+ }
+ }
+
+ template <typename P>
+ friend class btree;
+ friend class BtreeNodePeer;
+};
+
+template <typename Node, typename Reference, typename Pointer>
+struct btree_iterator {
+ private:
+ using key_type = typename Node::key_type;
+ using size_type = typename Node::size_type;
+ using params_type = typename Node::params_type;
+
+ using node_type = Node;
+ using normal_node = typename std::remove_const<Node>::type;
+ using const_node = const Node;
+ using normal_pointer = typename params_type::pointer;
+ using normal_reference = typename params_type::reference;
+ using const_pointer = typename params_type::const_pointer;
+ using const_reference = typename params_type::const_reference;
+
+ using iterator =
+ btree_iterator<normal_node, normal_reference, normal_pointer>;
+ using const_iterator =
+ btree_iterator<const_node, const_reference, const_pointer>;
+
+ public:
+ // These aliases are public for std::iterator_traits.
+ using difference_type = typename Node::difference_type;
+ using value_type = typename params_type::value_type;
+ using pointer = Pointer;
+ using reference = Reference;
+ using iterator_category = std::bidirectional_iterator_tag;
+
+ btree_iterator() : node(nullptr), position(-1) {}
+ btree_iterator(Node *n, int p) : node(n), position(p) {}
+
+ // NOTE: this SFINAE allows for implicit conversions from iterator to
+ // const_iterator, but it specifically avoids defining copy constructors so
+ // that btree_iterator can be trivially copyable. This is for performance and
+ // binary size reasons.
+ template <
+ typename N, typename R, typename P,
+ absl::enable_if_t<
+ std::is_same<btree_iterator<N, R, P>, iterator>::value &&
+ !std::is_same<btree_iterator<N, R, P>, btree_iterator>::value,
+ int> = 0>
+ btree_iterator(const btree_iterator<N, R, P> &x) // NOLINT
+ : node(x.node), position(x.position) {}
+
+ private:
+ // Increment/decrement the iterator.
+ void increment() {
+ if (node->leaf() && ++position < node->count()) {
+ return;
+ }
+ increment_slow();
+ }
+ void increment_slow();
+
+ void decrement() {
+ if (node->leaf() && --position >= 0) {
+ return;
+ }
+ decrement_slow();
+ }
+ void decrement_slow();
+
+ public:
+ bool operator==(const const_iterator &x) const {
+ return node == x.node && position == x.position;
+ }
+ bool operator!=(const const_iterator &x) const {
+ return node != x.node || position != x.position;
+ }
+
+ // Accessors for the key/value the iterator is pointing at.
+ reference operator*() const {
+ return node->value(position);
+ }
+ pointer operator->() const {
+ return &node->value(position);
+ }
+
+ btree_iterator& operator++() {
+ increment();
+ return *this;
+ }
+ btree_iterator& operator--() {
+ decrement();
+ return *this;
+ }
+ btree_iterator operator++(int) {
+ btree_iterator tmp = *this;
+ ++*this;
+ return tmp;
+ }
+ btree_iterator operator--(int) {
+ btree_iterator tmp = *this;
+ --*this;
+ return tmp;
+ }
+
+ private:
+ template <typename Params>
+ friend class btree;
+ template <typename N, typename R, typename P>
+ friend struct btree_iterator;
+ template <typename TreeType, typename CheckerType>
+ friend class base_checker;
+
+ const key_type &key() const { return node->key(position); }
+
+ // The node in the tree the iterator is pointing at.
+ Node *node;
+ // The position within the node of the tree the iterator is pointing at.
+ int position;
+};
+
+// Approximation of std::is_trivially_copyable (which is currently unsupported).
+template <typename T>
+using is_trivially_copyable = absl::conjunction<
+ absl::is_trivially_copy_constructible<T>,
+ absl::disjunction<absl::is_trivially_copy_assignable<T>,
+ absl::negation<std::is_copy_assignable<T>>>,
+ absl::is_trivially_destructible<T>>;
+
+template <typename Params>
+class btree {
+ using node_type = btree_node<Params>;
+ using is_key_compare_to = typename Params::is_key_compare_to;
+
+ template <typename K>
+ using const_lookup_key_reference = absl::conditional_t<
+ is_comparator_transparent<typename Params::key_compare>::value, const K &,
+ const typename Params::key_type &>;
+
+ enum {
+ kNodeValues = node_type::kNodeValues,
+ kMinNodeValues = kNodeValues / 2,
+ kExactMatch = node_type::kExactMatch,
+ kMatchMask = node_type::kMatchMask,
+ };
+
+ struct node_stats {
+ using size_type = typename Params::size_type;
+
+ node_stats(size_type l, size_type i)
+ : leaf_nodes(l),
+ internal_nodes(i) {
+ }
+
+ node_stats& operator+=(const node_stats &x) {
+ leaf_nodes += x.leaf_nodes;
+ internal_nodes += x.internal_nodes;
+ return *this;
+ }
+
+ size_type leaf_nodes;
+ size_type internal_nodes;
+ };
+
+ public:
+ using key_type = typename Params::key_type;
+ using mapped_type = typename Params::mapped_type;
+ using value_type = typename Params::value_type;
+ using size_type = typename Params::size_type;
+ using difference_type = typename Params::difference_type;
+ using key_compare = typename Params::key_compare;
+ using value_compare = typename Params::value_compare;
+ using allocator_type = typename Params::allocator_type;
+ using reference = typename Params::reference;
+ using const_reference = typename Params::const_reference;
+ using pointer = typename Params::pointer;
+ using const_pointer = typename Params::const_pointer;
+ using iterator = btree_iterator<node_type, reference, pointer>;
+ using const_iterator = typename iterator::const_iterator;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ // Internal types made public for use by btree_container types.
+ using params_type = Params;
+ using mutable_value_type = typename Params::mutable_value_type;
+ using slot_type = typename Params::slot_type;
+
+ private:
+ // Copies the values in x into this btree in their order in x.
+ // This btree must be empty before this method is called.
+ // This method is used in copy construction and copy assignment.
+ void copy_values_in_order(const btree &x);
+
+ public:
+ btree(const key_compare &comp, const allocator_type &alloc);
+
+ btree(const btree &x);
+ btree(btree &&x) noexcept
+ : root_(std::move(x.root_)),
+ rightmost_(absl::exchange(x.rightmost_, nullptr)),
+ size_(absl::exchange(x.size_, 0)) {
+ x.mutable_root() = nullptr;
+ }
+
+ ~btree() {
+ static_assert(std::is_nothrow_copy_constructible<key_compare>::value,
+ "Key comparison must be nothrow copy constructible");
+ static_assert(std::is_nothrow_copy_constructible<allocator_type>::value,
+ "Allocator must be nothrow copy constructible");
+ static_assert(is_trivially_copyable<iterator>::value,
+ "iterator not trivially copyable.");
+ clear();
+ }
+
+ // Assign the contents of x to *this.
+ btree &operator=(const btree &x);
+
+ btree &operator=(btree &&x) noexcept {
+ clear();
+ swap(x);
+ return *this;
+ }
+
+ iterator begin() {
+ return iterator(leftmost(), 0);
+ }
+ const_iterator begin() const {
+ return const_iterator(leftmost(), 0);
+ }
+ iterator end() {
+ return iterator(rightmost_,
+ rightmost_ != nullptr ? rightmost_->count() : 0);
+ }
+ const_iterator end() const {
+ return const_iterator(rightmost_,
+ rightmost_ != nullptr ? rightmost_->count() : 0);
+ }
+ reverse_iterator rbegin() {
+ return reverse_iterator(end());
+ }
+ const_reverse_iterator rbegin() const {
+ return const_reverse_iterator(end());
+ }
+ reverse_iterator rend() {
+ return reverse_iterator(begin());
+ }
+ const_reverse_iterator rend() const {
+ return const_reverse_iterator(begin());
+ }
+
+ // Finds the first element whose key is not less than key.
+ template <typename K>
+ iterator lower_bound(const K &key) {
+ return internal_end(
+ internal_lower_bound(key, iterator(root(), 0)));
+ }
+ template <typename K>
+ const_iterator lower_bound(const K &key) const {
+ return internal_end(
+ internal_lower_bound(key, const_iterator(root(), 0)));
+ }
+
+ // Finds the first element whose key is greater than key.
+ template <typename K>
+ iterator upper_bound(const K &key) {
+ return internal_end(
+ internal_upper_bound(key, iterator(root(), 0)));
+ }
+ template <typename K>
+ const_iterator upper_bound(const K &key) const {
+ return internal_end(
+ internal_upper_bound(key, const_iterator(root(), 0)));
+ }
+
+ // Finds the range of values which compare equal to key. The first member of
+ // the returned pair is equal to lower_bound(key). The second member pair of
+ // the pair is equal to upper_bound(key).
+ template <typename K>
+ std::pair<iterator, iterator> equal_range(const K &key) {
+ const_lookup_key_reference<K> lookup_key(key);
+ return std::make_pair(lower_bound(lookup_key), upper_bound(lookup_key));
+ }
+ template <typename K>
+ std::pair<const_iterator, const_iterator> equal_range(const K &key) const {
+ const_lookup_key_reference<K> lookup_key(key);
+ return std::make_pair(lower_bound(lookup_key), upper_bound(lookup_key));
+ }
+
+ // Inserts a value into the btree only if it does not already exist. The
+ // boolean return value indicates whether insertion succeeded or failed.
+ template <typename... Args>
+ std::pair<iterator, bool> insert_unique(const key_type &key, Args &&... args);
+
+ // Insert with hint. Check to see if the value should be placed immediately
+ // before position in the tree. If it does, then the insertion will take
+ // amortized constant time. If not, the insertion will take amortized
+ // logarithmic time as if a call to insert_unique(v) were made.
+ template <typename... Args>
+ iterator insert_hint_unique(iterator position, const key_type &key,
+ Args &&... args);
+
+ // Insert a range of values into the btree.
+ template <typename InputIterator>
+ void insert_iterator_unique(InputIterator b, InputIterator e);
+
+ // Inserts a value into the btree.
+ template <typename ValueType>
+ iterator insert_multi(const key_type &key, ValueType &&v);
+
+ // Inserts a value into the btree.
+ template <typename ValueType>
+ iterator insert_multi(ValueType &&v) {
+ return insert_multi(params_type::key(v), std::forward<ValueType>(v));
+ }
+
+ // Insert with hint. Check to see if the value should be placed immediately
+ // before position in the tree. If it does, then the insertion will take
+ // amortized constant time. If not, the insertion will take amortized
+ // logarithmic time as if a call to insert_multi(v) were made.
+ template <typename ValueType>
+ iterator insert_hint_multi(iterator position, ValueType &&v);
+
+ // Insert a range of values into the btree.
+ template <typename InputIterator>
+ void insert_iterator_multi(InputIterator b, InputIterator e);
+
+ // Erase the specified iterator from the btree. The iterator must be valid
+ // (i.e. not equal to end()). Return an iterator pointing to the node after
+ // the one that was erased (or end() if none exists).
+ iterator erase(iterator iter);
+
+ // Erases range. Returns the number of keys erased.
+ int erase(iterator begin, iterator end);
+
+ // Erases the specified key from the btree. Returns 1 if an element was
+ // erased and 0 otherwise.
+ template <typename K>
+ int erase_unique(const K &key);
+
+ // Erases all of the entries matching the specified key from the
+ // btree. Returns the number of elements erased.
+ template <typename K>
+ int erase_multi(const K &key);
+
+ // Finds the iterator corresponding to a key or returns end() if the key is
+ // not present.
+ template <typename K>
+ iterator find_unique(const K &key) {
+ return internal_end(
+ internal_find_unique(key, iterator(root(), 0)));
+ }
+ template <typename K>
+ const_iterator find_unique(const K &key) const {
+ return internal_end(
+ internal_find_unique(key, const_iterator(root(), 0)));
+ }
+ template <typename K>
+ iterator find_multi(const K &key) {
+ return internal_end(
+ internal_find_multi(key, iterator(root(), 0)));
+ }
+ template <typename K>
+ const_iterator find_multi(const K &key) const {
+ return internal_end(
+ internal_find_multi(key, const_iterator(root(), 0)));
+ }
+
+ // Returns a count of the number of times the key appears in the btree.
+ template <typename K>
+ size_type count_unique(const K &key) const {
+ const_iterator begin = internal_find_unique(
+ key, const_iterator(root(), 0));
+ if (!begin.node) {
+ // The key doesn't exist in the tree.
+ return 0;
+ }
+ return 1;
+ }
+ // Returns a count of the number of times the key appears in the btree.
+ template <typename K>
+ size_type count_multi(const K &key) const {
+ const auto range = equal_range(key);
+ return std::distance(range.first, range.second);
+ }
+
+ // Clear the btree, deleting all of the values it contains.
+ void clear();
+
+ // Swap the contents of *this and x.
+ void swap(btree &x);
+
+ const key_compare &key_comp() const noexcept {
+ return root_.template get<0>();
+ }
+ template <typename K, typename LK>
+ bool compare_keys(const K &x, const LK &y) const {
+ return bool_compare_keys(key_comp(), x, y);
+ }
+
+ value_compare value_comp() const { return value_compare(key_comp()); }
+
+ // Verifies the structure of the btree.
+ void verify() const;
+
+ // Size routines.
+ size_type size() const { return size_; }
+ size_type max_size() const { return std::numeric_limits<size_type>::max(); }
+ bool empty() const { return size_ == 0; }
+
+ // The height of the btree. An empty tree will have height 0.
+ size_type height() const {
+ size_type h = 0;
+ if (root()) {
+ // Count the length of the chain from the leftmost node up to the
+ // root. We actually count from the root back around to the level below
+ // the root, but the calculation is the same because of the circularity
+ // of that traversal.
+ const node_type *n = root();
+ do {
+ ++h;
+ n = n->parent();
+ } while (n != root());
+ }
+ return h;
+ }
+
+ // The number of internal, leaf and total nodes used by the btree.
+ size_type leaf_nodes() const {
+ return internal_stats(root()).leaf_nodes;
+ }
+ size_type internal_nodes() const {
+ return internal_stats(root()).internal_nodes;
+ }
+ size_type nodes() const {
+ node_stats stats = internal_stats(root());
+ return stats.leaf_nodes + stats.internal_nodes;
+ }
+
+ // The total number of bytes used by the btree.
+ size_type bytes_used() const {
+ node_stats stats = internal_stats(root());
+ if (stats.leaf_nodes == 1 && stats.internal_nodes == 0) {
+ return sizeof(*this) +
+ node_type::LeafSize(root()->max_count());
+ } else {
+ return sizeof(*this) +
+ stats.leaf_nodes * node_type::LeafSize() +
+ stats.internal_nodes * node_type::InternalSize();
+ }
+ }
+
+ // The average number of bytes used per value stored in the btree.
+ static double average_bytes_per_value() {
+ // Returns the number of bytes per value on a leaf node that is 75%
+ // full. Experimentally, this matches up nicely with the computed number of
+ // bytes per value in trees that had their values inserted in random order.
+ return node_type::LeafSize() / (kNodeValues * 0.75);
+ }
+
+ // The fullness of the btree. Computed as the number of elements in the btree
+ // divided by the maximum number of elements a tree with the current number
+ // of nodes could hold. A value of 1 indicates perfect space
+ // utilization. Smaller values indicate space wastage.
+ double fullness() const {
+ return static_cast<double>(size()) / (nodes() * kNodeValues);
+ }
+ // The overhead of the btree structure in bytes per node. Computed as the
+ // total number of bytes used by the btree minus the number of bytes used for
+ // storing elements divided by the number of elements.
+ double overhead() const {
+ if (empty()) {
+ return 0.0;
+ }
+ return (bytes_used() - size() * sizeof(value_type)) /
+ static_cast<double>(size());
+ }
+
+ // The allocator used by the btree.
+ allocator_type get_allocator() const {
+ return allocator();
+ }
+
+ private:
+ // Internal accessor routines.
+ node_type *root() { return root_.template get<2>(); }
+ const node_type *root() const { return root_.template get<2>(); }
+ node_type *&mutable_root() noexcept { return root_.template get<2>(); }
+ key_compare *mutable_key_comp() noexcept { return &root_.template get<0>(); }
+
+ // The leftmost node is stored as the parent of the root node.
+ node_type *leftmost() { return root() ? root()->parent() : nullptr; }
+ const node_type *leftmost() const {
+ return root() ? root()->parent() : nullptr;
+ }
+
+ // Allocator routines.
+ allocator_type *mutable_allocator() noexcept {
+ return &root_.template get<1>();
+ }
+ const allocator_type &allocator() const noexcept {
+ return root_.template get<1>();
+ }
+
+ // Allocates a correctly aligned node of at least size bytes using the
+ // allocator.
+ node_type *allocate(const size_type size) {
+ return reinterpret_cast<node_type *>(
+ absl::container_internal::Allocate<node_type::Alignment()>(
+ mutable_allocator(), size));
+ }
+
+ // Node creation/deletion routines.
+ node_type* new_internal_node(node_type *parent) {
+ node_type *p = allocate(node_type::InternalSize());
+ return node_type::init_internal(p, parent);
+ }
+ node_type* new_leaf_node(node_type *parent) {
+ node_type *p = allocate(node_type::LeafSize());
+ return node_type::init_leaf(p, parent, kNodeValues);
+ }
+ node_type *new_leaf_root_node(const int max_count) {
+ node_type *p = allocate(node_type::LeafSize(max_count));
+ return node_type::init_leaf(p, p, max_count);
+ }
+
+ // Deallocates a node of a certain size in bytes using the allocator.
+ void deallocate(const size_type size, node_type *node) {
+ absl::container_internal::Deallocate<node_type::Alignment()>(
+ mutable_allocator(), node, size);
+ }
+
+ void delete_internal_node(node_type *node) {
+ node->destroy(mutable_allocator());
+ deallocate(node_type::InternalSize(), node);
+ }
+ void delete_leaf_node(node_type *node) {
+ node->destroy(mutable_allocator());
+ deallocate(node_type::LeafSize(node->max_count()), node);
+ }
+
+ // Rebalances or splits the node iter points to.
+ void rebalance_or_split(iterator *iter);
+
+ // Merges the values of left, right and the delimiting key on their parent
+ // onto left, removing the delimiting key and deleting right.
+ void merge_nodes(node_type *left, node_type *right);
+
+ // Tries to merge node with its left or right sibling, and failing that,
+ // rebalance with its left or right sibling. Returns true if a merge
+ // occurred, at which point it is no longer valid to access node. Returns
+ // false if no merging took place.
+ bool try_merge_or_rebalance(iterator *iter);
+
+ // Tries to shrink the height of the tree by 1.
+ void try_shrink();
+
+ iterator internal_end(iterator iter) {
+ return iter.node ? iter : end();
+ }
+ const_iterator internal_end(const_iterator iter) const {
+ return iter.node ? iter : end();
+ }
+
+ // Emplaces a value into the btree immediately before iter. Requires that
+ // key(v) <= iter.key() and (--iter).key() <= key(v).
+ template <typename... Args>
+ iterator internal_emplace(iterator iter, Args &&... args);
+
+ // Returns an iterator pointing to the first value >= the value "iter" is
+ // pointing at. Note that "iter" might be pointing to an invalid location as
+ // iter.position == iter.node->count(). This routine simply moves iter up in
+ // the tree to a valid location.
+ template <typename IterType>
+ static IterType internal_last(IterType iter);
+
+ // Returns an iterator pointing to the leaf position at which key would
+ // reside in the tree. We provide 2 versions of internal_locate. The first
+ // version (internal_locate_plain_compare) always returns 0 for the second
+ // field of the pair. The second version (internal_locate_compare_to) is for
+ // the key-compare-to specialization and returns either kExactMatch (if the
+ // key was found in the tree) or -kExactMatch (if it wasn't) in the second
+ // field of the pair. The compare_to specialization allows the caller to
+ // avoid a subsequent comparison to determine if an exact match was made,
+ // speeding up string, cord and string_view keys.
+ template <typename K, typename IterType>
+ std::pair<IterType, int> internal_locate(
+ const K &key, IterType iter) const;
+ template <typename K, typename IterType>
+ std::pair<IterType, int> internal_locate_plain_compare(
+ const K &key, IterType iter) const;
+ template <typename K, typename IterType>
+ std::pair<IterType, int> internal_locate_compare_to(
+ const K &key, IterType iter) const;
+
+ // Internal routine which implements lower_bound().
+ template <typename K, typename IterType>
+ IterType internal_lower_bound(
+ const K &key, IterType iter) const;
+
+ // Internal routine which implements upper_bound().
+ template <typename K, typename IterType>
+ IterType internal_upper_bound(
+ const K &key, IterType iter) const;
+
+ // Internal routine which implements find_unique().
+ template <typename K, typename IterType>
+ IterType internal_find_unique(
+ const K &key, IterType iter) const;
+
+ // Internal routine which implements find_multi().
+ template <typename K, typename IterType>
+ IterType internal_find_multi(
+ const K &key, IterType iter) const;
+
+ // Deletes a node and all of its children.
+ void internal_clear(node_type *node);
+
+ // Verifies the tree structure of node.
+ int internal_verify(const node_type *node,
+ const key_type *lo, const key_type *hi) const;
+
+ node_stats internal_stats(const node_type *node) const {
+ if (!node) {
+ return node_stats(0, 0);
+ }
+ if (node->leaf()) {
+ return node_stats(1, 0);
+ }
+ node_stats res(0, 1);
+ for (int i = 0; i <= node->count(); ++i) {
+ res += internal_stats(node->child(i));
+ }
+ return res;
+ }
+
+ public:
+ // Exposed only for tests.
+ static bool testonly_uses_linear_node_search() {
+ return node_type::testonly_uses_linear_node_search();
+ }
+
+ private:
+ // We use compressed tuple in order to save space because key_compare and
+ // allocator_type are usually empty.
+ absl::container_internal::CompressedTuple<key_compare, allocator_type,
+ node_type *>
+ root_;
+
+ // A pointer to the rightmost node. Note that the leftmost node is stored as
+ // the root's parent.
+ node_type *rightmost_;
+
+ // Number of values.
+ size_type size_;
+
+ // Verify that key_compare returns an int or bool, as appropriate
+ // depending on the value of is_key_compare_to.
+ static_assert(std::is_same<absl::result_of_t<key_compare(key_type, key_type)>,
+ absl::conditional_t<is_key_compare_to::value, int,
+ bool>>::value,
+ "key comparison function must return bool");
+
+ // Note: We assert that kTargetValues, which is computed from
+ // Params::kTargetNodeSize, must fit the node_type::field_type.
+ static_assert(kNodeValues <
+ (1 << (8 * sizeof(typename node_type::field_type))),
+ "target node size too large");
+
+ // Test the assumption made in setting kNodeValueSpace.
+ static_assert(node_type::MinimumOverhead() >= sizeof(void *) + 4,
+ "node space assumption incorrect");
+};
+
+////
+// btree_node methods
+template <typename P>
+template <typename... Args>
+inline void btree_node<P>::emplace_value(const size_type i,
+ allocator_type *alloc,
+ Args &&... args) {
+ assert(i <= count());
+ // Shift old values to create space for new value and then construct it in
+ // place.
+ if (i < count()) {
+ value_init(count(), alloc, slot(count() - 1));
+ for (size_type j = count() - 1; j > i; --j)
+ slot_type::move(alloc, slot(j - 1), slot(j));
+ value_destroy(i, alloc);
+ }
+ value_init(i, alloc, std::forward<Args>(args)...);
+ set_count(count() + 1);
+
+ if (!leaf() && count() > i + 1) {
+ for (int j = count(); j > i + 1; --j) {
+ set_child(j, child(j - 1));
+ }
+ clear_child(i + 1);
+ }
+}
+
+template <typename P>
+inline void btree_node<P>::remove_value(const int i, allocator_type *alloc) {
+ if (!leaf() && count() > i + 1) {
+ assert(child(i + 1)->count() == 0);
+ for (size_type j = i + 1; j < count(); ++j) {
+ set_child(j, child(j + 1));
+ }
+ clear_child(count());
+ }
+
+ slot_type::move(alloc, slot(i + 1), slot(count()), slot(i));
+ value_destroy(count() - 1, alloc);
+ set_count(count() - 1);
+}
+
+template <typename P>
+void btree_node<P>::rebalance_right_to_left(const int to_move,
+ btree_node *right,
+ allocator_type *alloc) {
+ assert(parent() == right->parent());
+ assert(position() + 1 == right->position());
+ assert(right->count() >= count());
+ assert(to_move >= 1);
+ assert(to_move <= right->count());
+
+ // 1) Move the delimiting value in the parent to the left node.
+ value_init(count(), alloc, parent()->slot(position()));
+
+ // 2) Move the (to_move - 1) values from the right node to the left node.
+ right->uninitialized_move_n(to_move - 1, 0, count() + 1, this, alloc);
+
+ // 3) Move the new delimiting value to the parent from the right node.
+ slot_type::move(alloc, right->slot(to_move - 1), parent()->slot(position()));
+
+ // 4) Shift the values in the right node to their correct position.
+ slot_type::move(alloc, right->slot(to_move), right->slot(right->count()),
+ right->slot(0));
+
+ // 5) Destroy the now-empty to_move entries in the right node.
+ right->value_destroy_n(right->count() - to_move, to_move, alloc);
+
+ if (!leaf()) {
+ // Move the child pointers from the right to the left node.
+ for (int i = 0; i < to_move; ++i) {
+ init_child(count() + i + 1, right->child(i));
+ }
+ for (int i = 0; i <= right->count() - to_move; ++i) {
+ assert(i + to_move <= right->max_count());
+ right->init_child(i, right->child(i + to_move));
+ right->clear_child(i + to_move);
+ }
+ }
+
+ // Fixup the counts on the left and right nodes.
+ set_count(count() + to_move);
+ right->set_count(right->count() - to_move);
+}
+
+template <typename P>
+void btree_node<P>::rebalance_left_to_right(const int to_move,
+ btree_node *right,
+ allocator_type *alloc) {
+ assert(parent() == right->parent());
+ assert(position() + 1 == right->position());
+ assert(count() >= right->count());
+ assert(to_move >= 1);
+ assert(to_move <= count());
+
+ // Values in the right node are shifted to the right to make room for the
+ // new to_move values. Then, the delimiting value in the parent and the
+ // other (to_move - 1) values in the left node are moved into the right node.
+ // Lastly, a new delimiting value is moved from the left node into the
+ // parent, and the remaining empty left node entries are destroyed.
+
+ if (right->count() >= to_move) {
+ // The original location of the right->count() values are sufficient to hold
+ // the new to_move entries from the parent and left node.
+
+ // 1) Shift existing values in the right node to their correct positions.
+ right->uninitialized_move_n(to_move, right->count() - to_move,
+ right->count(), right, alloc);
+ for (slot_type *src = right->slot(right->count() - to_move - 1),
+ *dest = right->slot(right->count() - 1),
+ *end = right->slot(0);
+ src >= end; --src, --dest) {
+ slot_type::move(alloc, src, dest);
+ }
+
+ // 2) Move the delimiting value in the parent to the right node.
+ slot_type::move(alloc, parent()->slot(position()),
+ right->slot(to_move - 1));
+
+ // 3) Move the (to_move - 1) values from the left node to the right node.
+ slot_type::move(alloc, slot(count() - (to_move - 1)), slot(count()),
+ right->slot(0));
+ } else {
+ // The right node does not have enough initialized space to hold the new
+ // to_move entries, so part of them will move to uninitialized space.
+
+ // 1) Shift existing values in the right node to their correct positions.
+ right->uninitialized_move_n(right->count(), 0, to_move, right, alloc);
+
+ // 2) Move the delimiting value in the parent to the right node.
+ right->value_init(to_move - 1, alloc, parent()->slot(position()));
+
+ // 3) Move the (to_move - 1) values from the left node to the right node.
+ const size_type uninitialized_remaining = to_move - right->count() - 1;
+ uninitialized_move_n(uninitialized_remaining,
+ count() - uninitialized_remaining, right->count(),
+ right, alloc);
+ slot_type::move(alloc, slot(count() - (to_move - 1)),
+ slot(count() - uninitialized_remaining), right->slot(0));
+ }
+
+ // 4) Move the new delimiting value to the parent from the left node.
+ slot_type::move(alloc, slot(count() - to_move), parent()->slot(position()));
+
+ // 5) Destroy the now-empty to_move entries in the left node.
+ value_destroy_n(count() - to_move, to_move, alloc);
+
+ if (!leaf()) {
+ // Move the child pointers from the left to the right node.
+ for (int i = right->count(); i >= 0; --i) {
+ right->init_child(i + to_move, right->child(i));
+ right->clear_child(i);
+ }
+ for (int i = 1; i <= to_move; ++i) {
+ right->init_child(i - 1, child(count() - to_move + i));
+ clear_child(count() - to_move + i);
+ }
+ }
+
+ // Fixup the counts on the left and right nodes.
+ set_count(count() - to_move);
+ right->set_count(right->count() + to_move);
+}
+
+template <typename P>
+void btree_node<P>::split(const int insert_position, btree_node *dest,
+ allocator_type *alloc) {
+ assert(dest->count() == 0);
+ assert(max_count() == kNodeValues);
+
+ // We bias the split based on the position being inserted. If we're
+ // inserting at the beginning of the left node then bias the split to put
+ // more values on the right node. If we're inserting at the end of the
+ // right node then bias the split to put more values on the left node.
+ if (insert_position == 0) {
+ dest->set_count(count() - 1);
+ } else if (insert_position == kNodeValues) {
+ dest->set_count(0);
+ } else {
+ dest->set_count(count() / 2);
+ }
+ set_count(count() - dest->count());
+ assert(count() >= 1);
+
+ // Move values from the left sibling to the right sibling.
+ uninitialized_move_n(dest->count(), count(), 0, dest, alloc);
+
+ // Destroy the now-empty entries in the left node.
+ value_destroy_n(count(), dest->count(), alloc);
+
+ // The split key is the largest value in the left sibling.
+ set_count(count() - 1);
+ parent()->emplace_value(position(), alloc, slot(count()));
+ value_destroy(count(), alloc);
+ parent()->init_child(position() + 1, dest);
+
+ if (!leaf()) {
+ for (int i = 0; i <= dest->count(); ++i) {
+ assert(child(count() + i + 1) != nullptr);
+ dest->init_child(i, child(count() + i + 1));
+ clear_child(count() + i + 1);
+ }
+ }
+}
+
+template <typename P>
+void btree_node<P>::merge(btree_node *src, allocator_type *alloc) {
+ assert(parent() == src->parent());
+ assert(position() + 1 == src->position());
+
+ // Move the delimiting value to the left node.
+ value_init(count(), alloc, parent()->slot(position()));
+
+ // Move the values from the right to the left node.
+ src->uninitialized_move_n(src->count(), 0, count() + 1, this, alloc);
+
+ // Destroy the now-empty entries in the right node.
+ src->value_destroy_n(0, src->count(), alloc);
+
+ if (!leaf()) {
+ // Move the child pointers from the right to the left node.
+ for (int i = 0; i <= src->count(); ++i) {
+ init_child(count() + i + 1, src->child(i));
+ src->clear_child(i);
+ }
+ }
+
+ // Fixup the counts on the src and dest nodes.
+ set_count(1 + count() + src->count());
+ src->set_count(0);
+
+ // Remove the value on the parent node.
+ parent()->remove_value(position(), alloc);
+}
+
+template <typename P>
+void btree_node<P>::swap(btree_node *x, allocator_type *alloc) {
+ using std::swap;
+ assert(leaf() == x->leaf());
+
+ // Determine which is the smaller/larger node.
+ btree_node *smaller = this, *larger = x;
+ if (smaller->count() > larger->count()) {
+ swap(smaller, larger);
+ }
+
+ // Swap the values.
+ for (slot_type *a = smaller->slot(0), *b = larger->slot(0),
+ *end = a + smaller->count();
+ a != end; ++a, ++b) {
+ slot_type::swap(alloc, a, b);
+ }
+
+ // Move values that can't be swapped.
+ const size_type to_move = larger->count() - smaller->count();
+ larger->uninitialized_move_n(to_move, smaller->count(), smaller->count(),
+ smaller, alloc);
+ larger->value_destroy_n(smaller->count(), to_move, alloc);
+
+ if (!leaf()) {
+ // Swap the child pointers.
+ std::swap_ranges(&smaller->mutable_child(0),
+ &smaller->mutable_child(smaller->count() + 1),
+ &larger->mutable_child(0));
+ // Update swapped children's parent pointers.
+ int i = 0;
+ for (; i <= smaller->count(); ++i) {
+ smaller->child(i)->set_parent(smaller);
+ larger->child(i)->set_parent(larger);
+ }
+ // Move the child pointers that couldn't be swapped.
+ for (; i <= larger->count(); ++i) {
+ smaller->init_child(i, larger->child(i));
+ larger->clear_child(i);
+ }
+ }
+
+ // Swap the counts.
+ swap(mutable_count(), x->mutable_count());
+}
+
+////
+// btree_iterator methods
+template <typename N, typename R, typename P>
+void btree_iterator<N, R, P>::increment_slow() {
+ if (node->leaf()) {
+ assert(position >= node->count());
+ btree_iterator save(*this);
+ while (position == node->count() && !node->is_root()) {
+ assert(node->parent()->child(node->position()) == node);
+ position = node->position();
+ node = node->parent();
+ }
+ if (position == node->count()) {
+ *this = save;
+ }
+ } else {
+ assert(position < node->count());
+ node = node->child(position + 1);
+ while (!node->leaf()) {
+ node = node->child(0);
+ }
+ position = 0;
+ }
+}
+
+template <typename N, typename R, typename P>
+void btree_iterator<N, R, P>::decrement_slow() {
+ if (node->leaf()) {
+ assert(position <= -1);
+ btree_iterator save(*this);
+ while (position < 0 && !node->is_root()) {
+ assert(node->parent()->child(node->position()) == node);
+ position = node->position() - 1;
+ node = node->parent();
+ }
+ if (position < 0) {
+ *this = save;
+ }
+ } else {
+ assert(position >= 0);
+ node = node->child(position);
+ while (!node->leaf()) {
+ node = node->child(node->count());
+ }
+ position = node->count() - 1;
+ }
+}
+
+////
+// btree methods
+template <typename P>
+void btree<P>::copy_values_in_order(const btree &x) {
+ assert(empty());
+
+ // We can avoid key comparisons because we know the order of the
+ // values is the same order we'll store them in.
+ const_iterator iter = x.begin();
+ if (iter == x.end()) return;
+ insert_multi(*iter);
+ ++iter;
+ for (; iter != x.end(); ++iter) {
+ // If the btree is not empty, we can just insert the new value at the end
+ // of the tree!
+ internal_emplace(end(), *iter);
+ }
+}
+
+template <typename P>
+btree<P>::btree(const key_compare &comp, const allocator_type &alloc)
+ : root_(comp, alloc, nullptr), rightmost_(nullptr), size_(0) {}
+
+template <typename P>
+btree<P>::btree(const btree &x) : btree(x.key_comp(), x.allocator()) {
+ copy_values_in_order(x);
+}
+
+template <typename P>
+template <typename... Args>
+auto btree<P>::insert_unique(const key_type &key, Args &&... args)
+ -> std::pair<iterator, bool> {
+ if (empty()) {
+ mutable_root() = rightmost_ = new_leaf_root_node(1);
+ }
+
+ std::pair<iterator, int> res = internal_locate(key, iterator(root(), 0));
+ iterator &iter = res.first;
+ if (res.second == kExactMatch) {
+ // The key already exists in the tree, do nothing.
+ return std::make_pair(internal_last(iter), false);
+ } else if (!res.second) {
+ iterator last = internal_last(iter);
+ if (last.node && !compare_keys(key, last.key())) {
+ // The key already exists in the tree, do nothing.
+ return std::make_pair(last, false);
+ }
+ }
+
+ return std::make_pair(internal_emplace(iter, std::forward<Args>(args)...),
+ true);
+}
+
+template <typename P>
+template <typename... Args>
+inline auto btree<P>::insert_hint_unique(iterator position, const key_type &key,
+ Args &&... args) -> iterator {
+ if (!empty()) {
+ if (position == end() || compare_keys(key, position.key())) {
+ iterator prev = position;
+ if (position == begin() || compare_keys((--prev).key(), key)) {
+ // prev.key() < key < position.key()
+ return internal_emplace(position, std::forward<Args>(args)...);
+ }
+ } else if (compare_keys(position.key(), key)) {
+ iterator next = position;
+ ++next;
+ if (next == end() || compare_keys(key, next.key())) {
+ // position.key() < key < next.key()
+ return internal_emplace(next, std::forward<Args>(args)...);
+ }
+ } else {
+ // position.key() == key
+ return position;
+ }
+ }
+ return insert_unique(key, std::forward<Args>(args)...).first;
+}
+
+template <typename P>
+template <typename InputIterator>
+void btree<P>::insert_iterator_unique(InputIterator b, InputIterator e) {
+ for (; b != e; ++b) {
+ insert_hint_unique(end(), params_type::key(*b), *b);
+ }
+}
+
+template <typename P>
+template <typename ValueType>
+auto btree<P>::insert_multi(const key_type &key, ValueType &&v) -> iterator {
+ if (empty()) {
+ mutable_root() = rightmost_ = new_leaf_root_node(1);
+ }
+
+ iterator iter = internal_upper_bound(key, iterator(root(), 0));
+ if (!iter.node) {
+ iter = end();
+ }
+ return internal_emplace(iter, std::forward<ValueType>(v));
+}
+
+template <typename P>
+template <typename ValueType>
+auto btree<P>::insert_hint_multi(iterator position, ValueType &&v) -> iterator {
+ if (!empty()) {
+ const key_type &key = params_type::key(v);
+ if (position == end() || !compare_keys(position.key(), key)) {
+ iterator prev = position;
+ if (position == begin() || !compare_keys(key, (--prev).key())) {
+ // prev.key() <= key <= position.key()
+ return internal_emplace(position, std::forward<ValueType>(v));
+ }
+ } else {
+ iterator next = position;
+ ++next;
+ if (next == end() || !compare_keys(next.key(), key)) {
+ // position.key() < key <= next.key()
+ return internal_emplace(next, std::forward<ValueType>(v));
+ }
+ }
+ }
+ return insert_multi(std::forward<ValueType>(v));
+}
+
+template <typename P>
+template <typename InputIterator>
+void btree<P>::insert_iterator_multi(InputIterator b, InputIterator e) {
+ for (; b != e; ++b) {
+ insert_hint_multi(end(), *b);
+ }
+}
+
+template <typename P>
+auto btree<P>::operator=(const btree &x) -> btree & {
+ if (this != &x) {
+ clear();
+
+ *mutable_key_comp() = x.key_comp();
+ *mutable_allocator() = x.allocator();
+
+ copy_values_in_order(x);
+ }
+ return *this;
+}
+
+template <typename P>
+auto btree<P>::erase(iterator iter) -> iterator {
+ bool internal_delete = false;
+ if (!iter.node->leaf()) {
+ // Deletion of a value on an internal node. First, move the largest value
+ // from our left child here, then delete that position (in remove_value()
+ // below). We can get to the largest value from our left child by
+ // decrementing iter.
+ iterator internal_iter(iter);
+ --iter;
+ assert(iter.node->leaf());
+ assert(!compare_keys(internal_iter.key(), iter.key()));
+ slot_type::move(mutable_allocator(), iter.node->slot(iter.position),
+ internal_iter.node->slot(internal_iter.position));
+ internal_delete = true;
+ }
+
+ // Delete the key from the leaf.
+ iter.node->remove_value(iter.position, mutable_allocator());
+ --size_;
+
+ // We want to return the next value after the one we just erased. If we
+ // erased from an internal node (internal_delete == true), then the next
+ // value is ++(++iter). If we erased from a leaf node (internal_delete ==
+ // false) then the next value is ++iter. Note that ++iter may point to an
+ // internal node and the value in the internal node may move to a leaf node
+ // (iter.node) when rebalancing is performed at the leaf level.
+
+ // Merge/rebalance as we walk back up the tree.
+ iterator res(iter);
+ for (;;) {
+ if (iter.node == root()) {
+ try_shrink();
+ if (empty()) {
+ return end();
+ }
+ break;
+ }
+ if (iter.node->count() >= kMinNodeValues) {
+ break;
+ }
+ bool merged = try_merge_or_rebalance(&iter);
+ if (iter.node->leaf()) {
+ res = iter;
+ }
+ if (!merged) {
+ break;
+ }
+ iter.node = iter.node->parent();
+ }
+
+ // Adjust our return value. If we're pointing at the end of a node, advance
+ // the iterator.
+ if (res.position == res.node->count()) {
+ res.position = res.node->count() - 1;
+ ++res;
+ }
+ // If we erased from an internal node, advance the iterator.
+ if (internal_delete) {
+ ++res;
+ }
+ return res;
+}
+
+template <typename P>
+int btree<P>::erase(iterator begin, iterator end) {
+ int count = std::distance(begin, end);
+ for (int i = 0; i < count; i++) {
+ begin = erase(begin);
+ }
+ return count;
+}
+
+template <typename P> template <typename K>
+int btree<P>::erase_unique(const K &key) {
+ iterator iter = internal_find_unique(key, iterator(root(), 0));
+ if (!iter.node) {
+ // The key doesn't exist in the tree, return nothing done.
+ return 0;
+ }
+ erase(iter);
+ return 1;
+}
+
+template <typename P> template <typename K>
+int btree<P>::erase_multi(const K &key) {
+ iterator begin = internal_lower_bound(key, iterator(root(), 0));
+ if (!begin.node) {
+ // The key doesn't exist in the tree, return nothing done.
+ return 0;
+ }
+ // Delete all of the keys between begin and upper_bound(key).
+ iterator end = internal_end(
+ internal_upper_bound(key, iterator(root(), 0)));
+ return erase(begin, end);
+}
+
+template <typename P>
+void btree<P>::clear() {
+ if (root() != nullptr) {
+ internal_clear(root());
+ }
+ mutable_root() = nullptr;
+ rightmost_ = nullptr;
+ size_ = 0;
+}
+
+template <typename P>
+void btree<P>::swap(btree &x) {
+ using std::swap;
+ swap(root_, x.root_);
+ swap(rightmost_, x.rightmost_);
+ swap(size_, x.size_);
+}
+
+template <typename P>
+void btree<P>::verify() const {
+ if (root() != nullptr) {
+ assert(size() == internal_verify(root(), nullptr, nullptr));
+ assert(leftmost() == (++const_iterator(root(), -1)).node);
+ assert(rightmost_ == (--const_iterator(root(), root()->count())).node);
+ assert(leftmost()->leaf());
+ assert(rightmost_->leaf());
+ } else {
+ assert(empty());
+ assert(leftmost() == nullptr);
+ assert(rightmost_ == nullptr);
+ }
+}
+
+template <typename P>
+void btree<P>::rebalance_or_split(iterator *iter) {
+ node_type *&node = iter->node;
+ int &insert_position = iter->position;
+ assert(node->count() == node->max_count());
+ assert(kNodeValues == node->max_count());
+
+ // First try to make room on the node by rebalancing.
+ node_type *parent = node->parent();
+ if (node != root()) {
+ if (node->position() > 0) {
+ // Try rebalancing with our left sibling.
+ node_type *left = parent->child(node->position() - 1);
+ assert(left->max_count() == kNodeValues);
+ if (left->count() < kNodeValues) {
+ // We bias rebalancing based on the position being inserted. If we're
+ // inserting at the end of the right node then we bias rebalancing to
+ // fill up the left node.
+ int to_move = (kNodeValues - left->count()) /
+ (1 + (insert_position < kNodeValues));
+ to_move = std::max(1, to_move);
+
+ if (((insert_position - to_move) >= 0) ||
+ ((left->count() + to_move) < kNodeValues)) {
+ left->rebalance_right_to_left(to_move, node, mutable_allocator());
+
+ assert(node->max_count() - node->count() == to_move);
+ insert_position = insert_position - to_move;
+ if (insert_position < 0) {
+ insert_position = insert_position + left->count() + 1;
+ node = left;
+ }
+
+ assert(node->count() < node->max_count());
+ return;
+ }
+ }
+ }
+
+ if (node->position() < parent->count()) {
+ // Try rebalancing with our right sibling.
+ node_type *right = parent->child(node->position() + 1);
+ assert(right->max_count() == kNodeValues);
+ if (right->count() < kNodeValues) {
+ // We bias rebalancing based on the position being inserted. If we're
+ // inserting at the beginning of the left node then we bias rebalancing
+ // to fill up the right node.
+ int to_move =
+ (kNodeValues - right->count()) / (1 + (insert_position > 0));
+ to_move = std::max(1, to_move);
+
+ if ((insert_position <= (node->count() - to_move)) ||
+ ((right->count() + to_move) < kNodeValues)) {
+ node->rebalance_left_to_right(to_move, right, mutable_allocator());
+
+ if (insert_position > node->count()) {
+ insert_position = insert_position - node->count() - 1;
+ node = right;
+ }
+
+ assert(node->count() < node->max_count());
+ return;
+ }
+ }
+ }
+
+ // Rebalancing failed, make sure there is room on the parent node for a new
+ // value.
+ assert(parent->max_count() == kNodeValues);
+ if (parent->count() == kNodeValues) {
+ iterator parent_iter(node->parent(), node->position());
+ rebalance_or_split(&parent_iter);
+ }
+ } else {
+ // Rebalancing not possible because this is the root node.
+ // Create a new root node and set the current root node as the child of the
+ // new root.
+ parent = new_internal_node(parent);
+ parent->init_child(0, root());
+ mutable_root() = parent;
+ // If the former root was a leaf node, then it's now the rightmost node.
+ assert(!parent->child(0)->leaf() || parent->child(0) == rightmost_);
+ }
+
+ // Split the node.
+ node_type *split_node;
+ if (node->leaf()) {
+ split_node = new_leaf_node(parent);
+ node->split(insert_position, split_node, mutable_allocator());
+ if (rightmost_ == node) rightmost_ = split_node;
+ } else {
+ split_node = new_internal_node(parent);
+ node->split(insert_position, split_node, mutable_allocator());
+ }
+
+ if (insert_position > node->count()) {
+ insert_position = insert_position - node->count() - 1;
+ node = split_node;
+ }
+}
+
+template <typename P>
+void btree<P>::merge_nodes(node_type *left, node_type *right) {
+ left->merge(right, mutable_allocator());
+ if (right->leaf()) {
+ if (rightmost_ == right) rightmost_ = left;
+ delete_leaf_node(right);
+ } else {
+ delete_internal_node(right);
+ }
+}
+
+template <typename P>
+bool btree<P>::try_merge_or_rebalance(iterator *iter) {
+ node_type *parent = iter->node->parent();
+ if (iter->node->position() > 0) {
+ // Try merging with our left sibling.
+ node_type *left = parent->child(iter->node->position() - 1);
+ assert(left->max_count() == kNodeValues);
+ if ((1 + left->count() + iter->node->count()) <= kNodeValues) {
+ iter->position += 1 + left->count();
+ merge_nodes(left, iter->node);
+ iter->node = left;
+ return true;
+ }
+ }
+ if (iter->node->position() < parent->count()) {
+ // Try merging with our right sibling.
+ node_type *right = parent->child(iter->node->position() + 1);
+ assert(right->max_count() == kNodeValues);
+ if ((1 + iter->node->count() + right->count()) <= kNodeValues) {
+ merge_nodes(iter->node, right);
+ return true;
+ }
+ // Try rebalancing with our right sibling. We don't perform rebalancing if
+ // we deleted the first element from iter->node and the node is not
+ // empty. This is a small optimization for the common pattern of deleting
+ // from the front of the tree.
+ if ((right->count() > kMinNodeValues) &&
+ ((iter->node->count() == 0) ||
+ (iter->position > 0))) {
+ int to_move = (right->count() - iter->node->count()) / 2;
+ to_move = std::min(to_move, right->count() - 1);
+ iter->node->rebalance_right_to_left(to_move, right, mutable_allocator());
+ return false;
+ }
+ }
+ if (iter->node->position() > 0) {
+ // Try rebalancing with our left sibling. We don't perform rebalancing if
+ // we deleted the last element from iter->node and the node is not
+ // empty. This is a small optimization for the common pattern of deleting
+ // from the back of the tree.
+ node_type *left = parent->child(iter->node->position() - 1);
+ if ((left->count() > kMinNodeValues) &&
+ ((iter->node->count() == 0) ||
+ (iter->position < iter->node->count()))) {
+ int to_move = (left->count() - iter->node->count()) / 2;
+ to_move = std::min(to_move, left->count() - 1);
+ left->rebalance_left_to_right(to_move, iter->node, mutable_allocator());
+ iter->position += to_move;
+ return false;
+ }
+ }
+ return false;
+}
+
+template <typename P>
+void btree<P>::try_shrink() {
+ if (root()->count() > 0) {
+ return;
+ }
+ // Deleted the last item on the root node, shrink the height of the tree.
+ if (root()->leaf()) {
+ assert(size() == 0);
+ delete_leaf_node(root());
+ mutable_root() = nullptr;
+ rightmost_ = nullptr;
+ } else {
+ node_type *child = root()->child(0);
+ child->make_root();
+ delete_internal_node(root());
+ mutable_root() = child;
+ }
+}
+
+template <typename P> template <typename IterType>
+inline IterType btree<P>::internal_last(IterType iter) {
+ while (iter.node && iter.position == iter.node->count()) {
+ iter.position = iter.node->position();
+ iter.node = iter.node->parent();
+ if (iter.node->leaf()) {
+ iter.node = nullptr;
+ }
+ }
+ return iter;
+}
+
+template <typename P>
+template <typename... Args>
+inline auto btree<P>::internal_emplace(iterator iter, Args &&... args)
+ -> iterator {
+ if (!iter.node->leaf()) {
+ // We can't insert on an internal node. Instead, we'll insert after the
+ // previous value which is guaranteed to be on a leaf node.
+ --iter;
+ ++iter.position;
+ }
+ const int max_count = iter.node->max_count();
+ if (iter.node->count() == max_count) {
+ // Make room in the leaf for the new item.
+ if (max_count < kNodeValues) {
+ // Insertion into the root where the root is smaller than the full node
+ // size. Simply grow the size of the root node.
+ assert(iter.node == root());
+ iter.node = new_leaf_root_node(std::min<int>(kNodeValues, 2 * max_count));
+ iter.node->swap(root(), mutable_allocator());
+ delete_leaf_node(root());
+ mutable_root() = iter.node;
+ rightmost_ = iter.node;
+ } else {
+ rebalance_or_split(&iter);
+ }
+ }
+ iter.node->emplace_value(iter.position, mutable_allocator(),
+ std::forward<Args>(args)...);
+ ++size_;
+ return iter;
+}
+
+template <typename P> template <typename K, typename IterType>
+inline std::pair<IterType, int> btree<P>::internal_locate(
+ const K &key, IterType iter) const {
+ return is_key_compare_to::value ? internal_locate_compare_to(key, iter)
+ : internal_locate_plain_compare(key, iter);
+}
+
+template <typename P> template <typename K, typename IterType>
+inline std::pair<IterType, int> btree<P>::internal_locate_plain_compare(
+ const K &key, IterType iter) const {
+ for (;;) {
+ iter.position = iter.node->lower_bound(key, key_comp());
+ if (iter.node->leaf()) {
+ break;
+ }
+ iter.node = iter.node->child(iter.position);
+ }
+ return std::make_pair(iter, 0);
+}
+
+template <typename P> template <typename K, typename IterType>
+inline std::pair<IterType, int> btree<P>::internal_locate_compare_to(
+ const K &key, IterType iter) const {
+ for (;;) {
+ int res = iter.node->lower_bound(key, key_comp());
+ iter.position = res & kMatchMask;
+ if (res & kExactMatch) {
+ return std::make_pair(iter, static_cast<int>(kExactMatch));
+ }
+ if (iter.node->leaf()) {
+ break;
+ }
+ iter.node = iter.node->child(iter.position);
+ }
+ return std::make_pair(iter, -kExactMatch);
+}
+
+template <typename P> template <typename K, typename IterType>
+IterType btree<P>::internal_lower_bound(
+ const K &key, IterType iter) const {
+ const_lookup_key_reference<K> lookup_key(key);
+ if (iter.node) {
+ for (;;) {
+ iter.position =
+ iter.node->lower_bound(lookup_key, key_comp()) & kMatchMask;
+ if (iter.node->leaf()) {
+ break;
+ }
+ iter.node = iter.node->child(iter.position);
+ }
+ iter = internal_last(iter);
+ }
+ return iter;
+}
+
+template <typename P> template <typename K, typename IterType>
+IterType btree<P>::internal_upper_bound(
+ const K &key, IterType iter) const {
+ const_lookup_key_reference<K> lookup_key(key);
+ if (iter.node) {
+ for (;;) {
+ iter.position = iter.node->upper_bound(lookup_key, key_comp());
+ if (iter.node->leaf()) {
+ break;
+ }
+ iter.node = iter.node->child(iter.position);
+ }
+ iter = internal_last(iter);
+ }
+ return iter;
+}
+
+template <typename P> template <typename K, typename IterType>
+IterType btree<P>::internal_find_unique(
+ const K &key, IterType iter) const {
+ const_lookup_key_reference<K> lookup_key(key);
+ if (iter.node) {
+ std::pair<IterType, int> res = internal_locate(lookup_key, iter);
+ if (res.second == kExactMatch) {
+ return res.first;
+ }
+ if (!res.second) {
+ iter = internal_last(res.first);
+ if (iter.node && !compare_keys(lookup_key, iter.key())) {
+ return iter;
+ }
+ }
+ }
+ return IterType(nullptr, 0);
+}
+
+template <typename P> template <typename K, typename IterType>
+IterType btree<P>::internal_find_multi(
+ const K &key, IterType iter) const {
+ const_lookup_key_reference<K> lookup_key(key);
+ if (iter.node) {
+ iter = internal_lower_bound(lookup_key, iter);
+ if (iter.node) {
+ iter = internal_last(iter);
+ if (iter.node && !compare_keys(lookup_key, iter.key())) {
+ return iter;
+ }
+ }
+ }
+ return IterType(nullptr, 0);
+}
+
+template <typename P>
+void btree<P>::internal_clear(node_type *node) {
+ if (!node->leaf()) {
+ for (int i = 0; i <= node->count(); ++i) {
+ internal_clear(node->child(i));
+ }
+ delete_internal_node(node);
+ } else {
+ delete_leaf_node(node);
+ }
+}
+
+template <typename P>
+int btree<P>::internal_verify(
+ const node_type *node, const key_type *lo, const key_type *hi) const {
+ assert(node->count() > 0);
+ assert(node->count() <= node->max_count());
+ if (lo) {
+ assert(!compare_keys(node->key(0), *lo));
+ }
+ if (hi) {
+ assert(!compare_keys(*hi, node->key(node->count() - 1)));
+ }
+ for (int i = 1; i < node->count(); ++i) {
+ assert(!compare_keys(node->key(i), node->key(i - 1)));
+ }
+ int count = node->count();
+ if (!node->leaf()) {
+ for (int i = 0; i <= node->count(); ++i) {
+ assert(node->child(i) != nullptr);
+ assert(node->child(i)->parent() == node);
+ assert(node->child(i)->position() == i);
+ count += internal_verify(
+ node->child(i),
+ (i == 0) ? lo : &node->key(i - 1),
+ (i == node->count()) ? hi : &node->key(i));
+ }
+ }
+ return count;
+}
+
+} // namespace internal_btree
+} // namespace gtl
+
+
+#endif // S2_UTIL_GTL_BTREE_H_
--- /dev/null
+// Copyright 2007 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+//
+
+#ifndef S2_UTIL_GTL_BTREE_CONTAINER_H_
+#define S2_UTIL_GTL_BTREE_CONTAINER_H_
+
+#include <algorithm>
+#include <initializer_list>
+#include <utility>
+
+#include "s2/third_party/absl/base/internal/throw_delegate.h"
+#include "s2/util/gtl/btree.h" // IWYU pragma: export
+
+namespace gtl {
+namespace internal_btree {
+
+// A common base class for btree_set, btree_map, btree_multiset, and
+// btree_multimap.
+template <typename Tree>
+class btree_container {
+ public:
+ using key_type = typename Tree::key_type;
+ using value_type = typename Tree::value_type;
+ using size_type = typename Tree::size_type;
+ using difference_type = typename Tree::difference_type;
+ using key_compare = typename Tree::key_compare;
+ using value_compare = typename Tree::value_compare;
+ using allocator_type = typename Tree::allocator_type;
+ using reference = typename Tree::reference;
+ using const_reference = typename Tree::const_reference;
+ using pointer = typename Tree::pointer;
+ using const_pointer = typename Tree::const_pointer;
+ using iterator = typename Tree::iterator;
+ using const_iterator = typename Tree::const_iterator;
+ using reverse_iterator = typename Tree::reverse_iterator;
+ using const_reverse_iterator = typename Tree::const_reverse_iterator;
+
+ // Constructors/assignments.
+ btree_container() : tree_(key_compare(), allocator_type()) {}
+ explicit btree_container(const key_compare &comp,
+ const allocator_type &alloc = allocator_type())
+ : tree_(comp, alloc) {}
+ btree_container(const btree_container &x) = default;
+ btree_container(btree_container &&x) noexcept = default;
+ btree_container &operator=(const btree_container &x) = default;
+ btree_container &operator=(btree_container &&x) noexcept(
+ std::is_nothrow_move_assignable<Tree>::value) = default;
+
+ // Iterator routines.
+ iterator begin() { return tree_.begin(); }
+ const_iterator begin() const { return tree_.begin(); }
+ const_iterator cbegin() const { return tree_.begin(); }
+ iterator end() { return tree_.end(); }
+ const_iterator end() const { return tree_.end(); }
+ const_iterator cend() const { return tree_.end(); }
+ reverse_iterator rbegin() { return tree_.rbegin(); }
+ const_reverse_iterator rbegin() const { return tree_.rbegin(); }
+ const_reverse_iterator crbegin() const { return tree_.rbegin(); }
+ reverse_iterator rend() { return tree_.rend(); }
+ const_reverse_iterator rend() const { return tree_.rend(); }
+ const_reverse_iterator crend() const { return tree_.rend(); }
+
+ // Lookup routines.
+ template <typename K>
+ iterator lower_bound(const K &key) {
+ return tree_.lower_bound(key);
+ }
+ template <typename K>
+ const_iterator lower_bound(const K &key) const {
+ return tree_.lower_bound(key);
+ }
+ template <typename K>
+ iterator upper_bound(const K &key) {
+ return tree_.upper_bound(key);
+ }
+ template <typename K>
+ const_iterator upper_bound(const K &key) const {
+ return tree_.upper_bound(key);
+ }
+ template <typename K>
+ std::pair<iterator, iterator> equal_range(const K &key) {
+ return tree_.equal_range(key);
+ }
+ template <typename K>
+ std::pair<const_iterator, const_iterator> equal_range(const K &key) const {
+ return tree_.equal_range(key);
+ }
+
+ // Utility routines.
+ void clear() { tree_.clear(); }
+ void swap(btree_container &x) { tree_.swap(x.tree_); }
+ void verify() const { tree_.verify(); }
+
+ // Size routines.
+ size_type size() const { return tree_.size(); }
+ size_type max_size() const { return tree_.max_size(); }
+ bool empty() const { return tree_.empty(); }
+ size_type height() const { return tree_.height(); }
+ size_type internal_nodes() const { return tree_.internal_nodes(); }
+ size_type leaf_nodes() const { return tree_.leaf_nodes(); }
+ size_type nodes() const { return tree_.nodes(); }
+ size_type bytes_used() const { return tree_.bytes_used(); }
+ static double average_bytes_per_value() {
+ return Tree::average_bytes_per_value();
+ }
+ double fullness() const { return tree_.fullness(); }
+ double overhead() const { return tree_.overhead(); }
+
+ friend bool operator==(const btree_container &x, const btree_container &y) {
+ if (x.size() != y.size()) return false;
+ return std::equal(x.begin(), x.end(), y.begin());
+ }
+
+ friend bool operator!=(const btree_container &x, const btree_container &y) {
+ return !(x == y);
+ }
+
+ friend bool operator<(const btree_container &x, const btree_container &y) {
+ return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end());
+ }
+
+ friend bool operator>(const btree_container &x, const btree_container &y) {
+ return y < x;
+ }
+
+ friend bool operator<=(const btree_container &x, const btree_container &y) {
+ return !(y < x);
+ }
+
+ friend bool operator>=(const btree_container &x, const btree_container &y) {
+ return !(x < y);
+ }
+
+ // The allocator used by the btree.
+ allocator_type get_allocator() const { return tree_.get_allocator(); }
+
+ // The key comparator used by the btree.
+ key_compare key_comp() const { return tree_.key_comp(); }
+ value_compare value_comp() const { return tree_.value_comp(); }
+
+ // Support absl::Hash.
+ template <typename State>
+ friend State AbslHashValue(State h, const btree_container &b) {
+ for (const auto &v : b) {
+ h = State::combine(std::move(h), v);
+ }
+ return State::combine(std::move(h), b.size());
+ }
+
+ // Exposed only for tests.
+ static bool testonly_uses_linear_node_search() {
+ return Tree::testonly_uses_linear_node_search();
+ }
+
+ protected:
+ Tree tree_;
+};
+
+// A common base class for btree_set and btree_map.
+template <typename Tree>
+class btree_set_container : public btree_container<Tree> {
+ using super_type = btree_container<Tree>;
+ using mutable_value_type = typename Tree::mutable_value_type;
+ using params_type = typename Tree::params_type;
+ friend class BtreeNodePeer;
+
+ public:
+ using value_type = typename Tree::value_type;
+ using size_type = typename Tree::size_type;
+ using key_compare = typename Tree::key_compare;
+ using allocator_type = typename Tree::allocator_type;
+ using iterator = typename Tree::iterator;
+ using const_iterator = typename Tree::const_iterator;
+
+ // Inherit constructors.
+ using super_type::super_type;
+ btree_set_container() {}
+
+ // Range constructor.
+ template <class InputIterator>
+ btree_set_container(InputIterator b, InputIterator e,
+ const key_compare &comp = key_compare(),
+ const allocator_type &alloc = allocator_type())
+ : super_type(comp, alloc) {
+ insert(b, e);
+ }
+
+ // Initializer list constructor.
+ btree_set_container(std::initializer_list<value_type> init,
+ const key_compare &comp = key_compare(),
+ const allocator_type &alloc = allocator_type())
+ : btree_set_container(init.begin(), init.end(), comp, alloc) {}
+
+ // Lookup routines.
+ template <typename K>
+ iterator find(const K &key) {
+ return this->tree_.find_unique(key);
+ }
+ template <typename K>
+ const_iterator find(const K &key) const {
+ return this->tree_.find_unique(key);
+ }
+ template <typename K>
+ size_type count(const K &key) const {
+ return this->tree_.count_unique(key);
+ }
+
+ // Insertion routines.
+ std::pair<iterator, bool> insert(const value_type &x) {
+ return this->tree_.insert_unique(params_type::key(x), x);
+ }
+ std::pair<iterator, bool> insert(value_type &&x) {
+ return this->tree_.insert_unique(params_type::key(x), std::move(x));
+ }
+ template <typename... Args>
+ std::pair<iterator, bool> emplace(Args &&... args) {
+ mutable_value_type v(std::forward<Args>(args)...);
+ return this->tree_.insert_unique(params_type::key(v), std::move(v));
+ }
+ iterator insert(iterator position, const value_type &x) {
+ return this->tree_.insert_hint_unique(position, params_type::key(x), x);
+ }
+ iterator insert(iterator position, value_type &&x) {
+ return this->tree_.insert_hint_unique(position, params_type::key(x),
+ std::move(x));
+ }
+ template <typename... Args>
+ iterator emplace_hint(iterator position, Args &&... args) {
+ mutable_value_type v(std::forward<Args>(args)...);
+ return this->tree_.insert_hint_unique(position, params_type::key(v),
+ std::move(v));
+ }
+ template <typename InputIterator>
+ void insert(InputIterator b, InputIterator e) {
+ this->tree_.insert_iterator_unique(b, e);
+ }
+ void insert(std::initializer_list<value_type> init) {
+ this->tree_.insert_iterator_unique(init.begin(), init.end());
+ }
+
+ // Deletion routines.
+ template <typename K>
+ int erase(const K &key) {
+ return this->tree_.erase_unique(key);
+ }
+ // Erase the specified iterator from the btree. The iterator must be valid
+ // (i.e. not equal to end()). Return an iterator pointing to the node after
+ // the one that was erased (or end() if none exists).
+ iterator erase(const iterator &iter) { return this->tree_.erase(iter); }
+ void erase(const iterator &first, const iterator &last) {
+ this->tree_.erase(first, last);
+ }
+};
+
+// Base class for btree_map.
+template <typename Tree>
+class btree_map_container : public btree_set_container<Tree> {
+ using super_type = btree_set_container<Tree>;
+
+ public:
+ using key_type = typename Tree::key_type;
+ using mapped_type = typename Tree::mapped_type;
+ using value_type = typename Tree::value_type;
+ using key_compare = typename Tree::key_compare;
+ using allocator_type = typename Tree::allocator_type;
+
+ // Inherit constructors.
+ using super_type::super_type;
+ btree_map_container() {}
+
+ // Insertion routines.
+ mapped_type &operator[](const key_type &key) {
+ return this->tree_
+ .insert_unique(key, std::piecewise_construct,
+ std::forward_as_tuple(key), std::forward_as_tuple())
+ .first->second;
+ }
+ mapped_type &operator[](key_type &&key) {
+ return this->tree_
+ .insert_unique(key, std::piecewise_construct,
+ std::forward_as_tuple(std::move(key)),
+ std::forward_as_tuple())
+ .first->second;
+ }
+
+ mapped_type &at(const key_type &key) {
+ auto it = this->find(key);
+ if (it == this->end())
+ absl::base_internal::ThrowStdOutOfRange("btree_map::at");
+ return it->second;
+ }
+ const mapped_type &at(const key_type &key) const {
+ auto it = this->find(key);
+ if (it == this->end())
+ absl::base_internal::ThrowStdOutOfRange("btree_map::at");
+ return it->second;
+ }
+};
+
+// A common base class for btree_multiset and btree_multimap.
+template <typename Tree>
+class btree_multiset_container : public btree_container<Tree> {
+ using super_type = btree_container<Tree>;
+
+ public:
+ using key_type = typename Tree::key_type;
+ using value_type = typename Tree::value_type;
+ using mapped_type = typename Tree::mapped_type;
+ using size_type = typename Tree::size_type;
+ using key_compare = typename Tree::key_compare;
+ using allocator_type = typename Tree::allocator_type;
+ using iterator = typename Tree::iterator;
+ using const_iterator = typename Tree::const_iterator;
+
+ // Inherit constructors.
+ using super_type::super_type;
+ btree_multiset_container() {}
+
+ // Range constructor.
+ template <class InputIterator>
+ btree_multiset_container(InputIterator b, InputIterator e,
+ const key_compare &comp = key_compare(),
+ const allocator_type &alloc = allocator_type())
+ : super_type(comp, alloc) {
+ insert(b, e);
+ }
+
+ // Initializer list constructor.
+ btree_multiset_container(std::initializer_list<value_type> init,
+ const key_compare &comp = key_compare(),
+ const allocator_type &alloc = allocator_type())
+ : btree_multiset_container(init.begin(), init.end(), comp, alloc) {}
+
+ // Lookup routines.
+ template <typename K>
+ iterator find(const K &key) {
+ return this->tree_.find_multi(key);
+ }
+ template <typename K>
+ const_iterator find(const K &key) const {
+ return this->tree_.find_multi(key);
+ }
+ template <typename K>
+ size_type count(const K &key) const {
+ return this->tree_.count_multi(key);
+ }
+
+ // Insertion routines.
+ iterator insert(const value_type &x) { return this->tree_.insert_multi(x); }
+ iterator insert(value_type &&x) {
+ return this->tree_.insert_multi(std::move(x));
+ }
+ iterator insert(iterator position, const value_type &x) {
+ return this->tree_.insert_hint_multi(position, x);
+ }
+ iterator insert(iterator position, value_type &&x) {
+ return this->tree_.insert_hint_multi(position, std::move(x));
+ }
+ template <typename InputIterator>
+ void insert(InputIterator b, InputIterator e) {
+ this->tree_.insert_iterator_multi(b, e);
+ }
+ void insert(std::initializer_list<value_type> init) {
+ this->tree_.insert_iterator_multi(init.begin(), init.end());
+ }
+
+ // Deletion routines.
+ template <typename K>
+ int erase(const K &key) {
+ return this->tree_.erase_multi(key);
+ }
+ // Erase the specified iterator from the btree. The iterator must be valid
+ // (i.e. not equal to end()). Return an iterator pointing to the node after
+ // the one that was erased (or end() if none exists).
+ iterator erase(const iterator &iter) { return this->tree_.erase(iter); }
+ void erase(const iterator &first, const iterator &last) {
+ this->tree_.erase(first, last);
+ }
+};
+
+// A base class for btree_multimap.
+template <typename Tree>
+class btree_multimap_container : public btree_multiset_container<Tree> {
+ using super_type = btree_multiset_container<Tree>;
+
+ public:
+ using mapped_type = typename Tree::mapped_type;
+
+ // Inherit constructors.
+ using super_type::super_type;
+ btree_multimap_container() {}
+};
+
+} // namespace internal_btree
+} // namespace gtl
+
+#endif // S2_UTIL_GTL_BTREE_CONTAINER_H_
--- /dev/null
+// Copyright 2007 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+//
+// A btree_map<> implements the STL unique sorted associative container
+// interface and the pair associative container interface (a.k.a map<>) using a
+// btree. A btree_multimap<> implements the STL multiple sorted associative
+// container interface and the pair associative container interface (a.k.a
+// multimap<>) using a btree. See btree.h for details of the btree
+// implementation and caveats.
+//
+
+#ifndef S2_UTIL_GTL_BTREE_MAP_H_
+#define S2_UTIL_GTL_BTREE_MAP_H_
+
+#include <algorithm>
+#include <functional>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "s2/util/gtl/btree.h" // IWYU pragma: export
+#include "s2/util/gtl/btree_container.h" // IWYU pragma: export
+
+namespace gtl {
+
+template <typename Key, typename Value, typename Compare = std::less<Key>,
+ typename Alloc = std::allocator<std::pair<const Key, Value>>,
+ int TargetNodeSize = 256>
+class btree_map
+ : public internal_btree::btree_map_container<
+ internal_btree::btree<internal_btree::map_params<
+ Key, Value, Compare, Alloc, TargetNodeSize, /*Multi=*/false>>> {
+ using Base = typename btree_map::btree_map_container;
+
+ public:
+ btree_map() {}
+ using Base::Base;
+};
+
+template <typename K, typename V, typename C, typename A, int T>
+void swap(btree_map<K, V, C, A, T> &x, btree_map<K, V, C, A, T> &y) {
+ return x.swap(y);
+}
+
+template <typename Key, typename Value, typename Compare = std::less<Key>,
+ typename Alloc = std::allocator<std::pair<const Key, Value>>,
+ int TargetNodeSize = 256>
+class btree_multimap
+ : public internal_btree::btree_multimap_container<
+ internal_btree::btree<internal_btree::map_params<
+ Key, Value, Compare, Alloc, TargetNodeSize, /*Multi=*/true>>> {
+ using Base = typename btree_multimap::btree_multimap_container;
+
+ public:
+ btree_multimap() {}
+ using Base::Base;
+};
+
+template <typename K, typename V, typename C, typename A, int T>
+void swap(btree_multimap<K, V, C, A, T> &x, btree_multimap<K, V, C, A, T> &y) {
+ return x.swap(y);
+}
+
+} // namespace gtl
+
+#endif // S2_UTIL_GTL_BTREE_MAP_H_
--- /dev/null
+// Copyright 2007 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+//
+// A btree_set<> implements the STL unique sorted associative container
+// interface (a.k.a set<>) using a btree. A btree_multiset<> implements the STL
+// multiple sorted associative container interface (a.k.a multiset<>) using a
+// btree. See btree.h for details of the btree implementation and caveats.
+//
+
+#ifndef S2_UTIL_GTL_BTREE_SET_H_
+#define S2_UTIL_GTL_BTREE_SET_H_
+
+#include <functional>
+#include <memory>
+#include <string>
+
+#include "s2/util/gtl/btree.h" // IWYU pragma: export
+#include "s2/util/gtl/btree_container.h" // IWYU pragma: export
+
+namespace gtl {
+
+template <typename Key, typename Compare = std::less<Key>,
+ typename Alloc = std::allocator<Key>, int TargetNodeSize = 256>
+class btree_set
+ : public internal_btree::btree_set_container<
+ internal_btree::btree<internal_btree::set_params<
+ Key, Compare, Alloc, TargetNodeSize, /*Multi=*/false>>> {
+ using Base = typename btree_set::btree_set_container;
+
+ public:
+ btree_set() {}
+ using Base::Base;
+};
+
+template <typename K, typename C, typename A, int T>
+void swap(btree_set<K, C, A, T> &x, btree_set<K, C, A, T> &y) {
+ return x.swap(y);
+}
+
+template <typename Key, typename Compare = std::less<Key>,
+ typename Alloc = std::allocator<Key>, int TargetNodeSize = 256>
+class btree_multiset
+ : public internal_btree::btree_multiset_container<
+ internal_btree::btree<internal_btree::set_params<
+ Key, Compare, Alloc, TargetNodeSize, /*Multi=*/true>>> {
+ using Base = typename btree_multiset::btree_multiset_container;
+
+ public:
+ btree_multiset() {}
+ using Base::Base;
+};
+
+template <typename K, typename C, typename A, int T>
+void swap(btree_multiset<K, C, A, T> &x, btree_multiset<K, C, A, T> &y) {
+ return x.swap(y);
+}
+
+} // namespace gtl
+
+#endif // S2_UTIL_GTL_BTREE_SET_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+
+// compact_array is a more memory-efficient implementation of std::vector.
+// It uses a pointer with an integer that stores both size and capacity.
+//
+// Implementation details:
+//
+// compact_array is a small-overhead STL-like Collection, but can only be used
+// for types with trivial copy, assign, and destructor. It only takes 16
+// bytes for 64-bit binary (instead of the typical 24-bytes for vector).
+// Its size can grow to 2^24 (16M) elements. compact_array is memory
+// efficient when it is small, and CPU-efficient for growing a large array.
+// It does this by keeping both the size and capacity. When the size is less
+// than 64, the capacity is exactly as reserved, and grows linearly. Once the
+// size grows bigger than 64, the capacity grows exponentially.
+//
+// IMPORTANT: compact_array_base does not support a constructor and destructor
+// because it is designed to be used in "union". The replacements are
+// Construct() and Destruct() which MUST be called explicitly. If you need
+// constructor and destructor, use compact_array instead.
+//
+
+#ifndef S2_UTIL_GTL_COMPACT_ARRAY_H_
+#define S2_UTIL_GTL_COMPACT_ARRAY_H_
+
+#include <cstddef>
+#include <cstring>
+#include <sys/types.h>
+#include <algorithm>
+#include <iterator>
+#include <memory>
+#include <ostream> // NOLINT
+#include <stdexcept>
+#include <type_traits>
+#include <utility>
+
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/base/port.h"
+#include "s2/third_party/absl/meta/type_traits.h"
+#include "s2/util/bits/bits.h"
+#include "s2/util/gtl/container_logging.h"
+
+namespace gtl {
+
+template <typename T, typename A = std::allocator<T> >
+class compact_array_base {
+ private:
+ // The number of bits for the variable size_ and capacity_
+ static const int kSizeNumBits = 24;
+ static const int kCapacityNumBits = 6;
+ // Where capacity_ becomes an exponent (of 2) instead of the exact value
+ static const int kExponentStart = (1 << kCapacityNumBits);
+ // kMaxSize is the maximum size this array can grow.
+ static const int kMaxSize = (1 << kSizeNumBits) - 1;
+
+#ifdef IS_LITTLE_ENDIAN
+ uint32 size_ : kSizeNumBits; // number of valid items in the array
+ uint32 capacity_ : kCapacityNumBits; // allocated array size
+ uint32 is_exponent_ : 1; // whether capacity_ is an exponent
+
+ // This object might share memory representation (ie. union) with
+ // other data structures. We reserved the DO_NOT_USE (32nd bit in
+ // little endian format) to be used as a tag.
+ uint32 DO_NOT_USE : 1;
+#else
+ uint32 DO_NOT_USE : 1;
+ uint32 is_exponent_ : 1;
+ uint32 capacity_ : kCapacityNumBits;
+ uint32 size_ : kSizeNumBits;
+#endif
+
+ // Opportunistically consider allowing inlined elements.
+ // dd: this has to be disabled to pass CRAN checks, since there is a
+ // (potentially) zero-length array that is not the last element of the class (so
+ // this can't be silenced using __extension__)
+#if defined(_LP64) && defined(__GNUC__) && false
+ // With 64-bit pointers, our approach is to form a 16-byte struct:
+ // [5 bytes for size, capacity, is_exponent and is_inlined]
+ // [3 bytes of padding or inlined elements]
+ // [8 bytes of more inlined elements or a pointer]
+ // We require 0-length arrays to take 0 bytes, and no strict aliasing. There
+ // should be no compiler-inserted padding between any of our members.
+ enum {
+ kMaxInlinedBytes = 11,
+ kInlined = kMaxInlinedBytes / sizeof(T),
+ kActualInlinedBytes = kInlined * sizeof(T),
+ kUnusedPaddingBytes = (kMaxInlinedBytes - kActualInlinedBytes) > 3 ?
+ 3 : (kMaxInlinedBytes - kActualInlinedBytes)
+ };
+
+ T* Array() { return IsInlined() ? InlinedSpace() : pointer_; }
+ void SetArray(T* p) {
+ static_assert(sizeof(*this) == 16, "size assumption");
+ static_assert(sizeof(this) == 8, "pointer size assumption");
+ is_inlined_ = false;
+ pointer_ = p;
+ }
+ void SetInlined() {
+ S2_DCHECK_LE(capacity(), kInlined);
+ is_inlined_ = true;
+ }
+ T* InlinedSpace() { return reinterpret_cast<T*>(inlined_elements_); }
+
+ bool is_inlined_; // If false, the last 8 bytes of *this are a pointer.
+
+ // After is_inlined_, the next field may not be sufficiently aligned to store
+ // an object of type T. Pad it out with (unaligned) chars.
+ char unused_padding_[kUnusedPaddingBytes];
+
+ // inlined_elements_ stores the first N elements, potentially as few as zero.
+ __extension__ char inlined_elements_[3 - kUnusedPaddingBytes];
+
+ // compact_array_base itself is at least as aligned as a T* because of the
+ // T* member inside this union. The only reason to split inlined_elements_
+ // into two pieces is to have a place to put this T* member.
+ union {
+ T* pointer_;
+ char more_inlined_elements_[sizeof(T*)];
+ };
+#else
+ enum { kInlined = 0, is_inlined_ = false };
+ T* Array() { return first_; }
+ void SetArray(T* p) { first_ = p; }
+ void SetInlined() { S2_LOG(FATAL); }
+ T* InlinedSpace() { return nullptr; }
+
+ // The pointer to the actual data array.
+ T* first_;
+#endif
+ bool IsInlined() const { return is_inlined_; }
+ const T* ConstArray() const {
+ return const_cast<compact_array_base<T, A>*>(this)->Array();
+ }
+
+ typedef typename A::template rebind<T>::other value_allocator_type;
+
+ public:
+ typedef T value_type;
+ typedef A allocator_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef uint32 size_type;
+ typedef ptrdiff_t difference_type;
+
+ typedef value_type* iterator;
+ typedef const value_type* const_iterator;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+ // Init() replace the default constructors; so it can be used in "union".
+ // This means Init() must be called for every new compact_array_base
+ void Init() noexcept { memset(this, 0, sizeof(*this)); }
+
+ // Construct an array of size n and initialize the values to v.
+ // Any old contents, if heap-allocated, will be leaked.
+ void Construct(size_type n, const value_type& v = value_type()) {
+ Init();
+ value_init(n, v);
+ }
+
+ // See 23.1.1/9 in the C++ standard for an explanation.
+ template <typename Iterator>
+ void Copy(Iterator first, Iterator last) {
+ Init();
+ typedef typename std::is_integral<Iterator>::type Int;
+ initialize(first, last, Int());
+ }
+
+ void CopyFrom(const compact_array_base& v) {
+ Init();
+ initialize(v.begin(), v.end(), std::false_type());
+ }
+
+ compact_array_base& AssignFrom(const compact_array_base& v) {
+ // Safe for self-assignment, which is rare.
+ // Optimized to use existing allocated space.
+ // Also to use assignment instead of copying where possible.
+ if (size() < v.size()) { // grow
+ reserve(v.size());
+ std::copy(v.begin(), v.begin() + size(), begin());
+ insert(end(), v.begin() + size(), v.end());
+ } else { // maybe shrink
+ erase(begin() + v.size(), end());
+ std::copy(v.begin(), v.end(), begin());
+ }
+ return *this;
+ }
+
+ // Deallocate the whole array.
+ void Destruct() {
+ if (!MayBeInlined() || Array() != InlinedSpace()) {
+ value_allocator_type allocator;
+ allocator.deallocate(Array(), capacity());
+ }
+ Init();
+ }
+
+ // Safe against self-swapping.
+ // copying/destruction of compact_array_base is fairly trivial as the type
+ // was designed to be useable in a C++98 union.
+ void swap(compact_array_base& v) noexcept {
+ compact_array_base tmp = *this;
+ *this = v;
+ v = tmp;
+ }
+
+ // The number of active items in the array.
+ size_type size() const { return size_; }
+ bool empty() const { return size() == 0; }
+
+ // Maximum size that this data structure can hold.
+ static size_type max_size() { return kMaxSize; }
+
+ static bool MayBeInlined() { return kInlined > 0; }
+
+ public: // Container interface (tables 65,66).
+ iterator begin() { return Array(); }
+ iterator end() { return Array() + size(); }
+ const_iterator begin() const { return ConstArray(); }
+ const_iterator end() const { return ConstArray() + size(); }
+
+ reverse_iterator rbegin() { return reverse_iterator(end()); }
+ reverse_iterator rend() { return reverse_iterator(Array()); }
+ const_reverse_iterator rbegin() const {
+ return const_reverse_iterator(end());
+ }
+ const_reverse_iterator rend() const {
+ return const_reverse_iterator(ConstArray());
+ }
+
+ private:
+ // This Insert() is private because it might return the end().
+ iterator Insert(const_iterator p, const value_type& v) {
+ if (size() >= kMaxSize) {
+ throw std::length_error("compact_array size exceeded");
+ }
+ iterator r = make_hole(p, 1);
+ *r = v;
+ return r;
+ }
+
+ public: // Sequence operations, table 67.
+ iterator insert(const_iterator p, const value_type& v) {
+ return Insert(p, v);
+ }
+
+ void insert(const_iterator p, size_type n, const value_type& v) {
+ if (n + size() > kMaxSize) {
+ throw std::length_error("compact_array size exceeded");
+ }
+ value_insert(p, n, v);
+ }
+
+ // See 23.1.1/9 in the C++ standard for an explanation.
+ template <typename Iterator>
+ void insert(const_iterator p, Iterator first, Iterator last) {
+ typedef typename std::is_integral<Iterator>::type Int;
+ insert(p, first, last, Int());
+ }
+
+ iterator erase(const_iterator p) {
+ size_type index = p - begin();
+ erase_aux(p, 1);
+ return begin() + index;
+ }
+
+ iterator erase(const_iterator first, const_iterator last) {
+ size_type index = first - begin();
+ erase_aux(first, last - first);
+ return begin() + index;
+ }
+
+ // clear just resets the size to 0, without deallocating the storage.
+ // To deallocate the array, use Destruct().
+ void clear() {
+ set_size(0);
+ }
+
+ reference front() { return begin()[0]; }
+ const_reference front() const { return begin()[0]; }
+ reference back() { return end()[-1]; }
+ const_reference back() const { return end()[-1]; }
+
+ void push_back(const value_type& v) {
+ iterator p = make_hole(end(), 1);
+ *p = v;
+ }
+ void pop_back() {
+ erase_aux(end()-1, 1);
+ }
+
+ reference operator[](size_type n) {
+ S2_DCHECK_LT(n, size_);
+ return Array()[n];
+ }
+
+ const_reference operator[](size_type n) const {
+ S2_DCHECK_LT(n, size_);
+ return ConstArray()[n];
+ }
+
+ reference at(size_type n) {
+ if (n >= size_) {
+ throw std::out_of_range("compact_array index out of range");
+ }
+ return Array()[n];
+ }
+
+ const_reference at(size_type n) const {
+ if (n >= size_) {
+ throw std::out_of_range("compact_array index out of range");
+ }
+ return ConstArray()[n];
+ }
+
+ // Preallocate the array of size n. Only changes the capacity, not size.
+ void reserve(int n) {
+ reallocate(n);
+ }
+
+ size_type capacity() const {
+ return is_exponent_ ? (1 << capacity_) : capacity_;
+ }
+
+ void resize(size_type n) {
+ if (n > capacity()) reserve(n);
+ // resize(n) is the only place in the class that exposes uninitialized
+ // memory as live elements, so call a constructor for each element if
+ // needed.
+ // Destroying elements on shrinking resize isn't a concern, since the
+ // value_type must be trivially destructible.
+ if (n > size() &&
+ !absl::is_trivially_default_constructible<value_type>::value) {
+ // Increasing size would expose unconstructed elements.
+ value_type *new_end = Array() + n;
+ for (value_type *p = Array() + size(); p != new_end; ++p)
+ new (p) value_type();
+ }
+ set_size(n);
+ }
+
+ private: // Low-level helper functions.
+ void set_size(size_type n) {
+ S2_DCHECK_LE(n, capacity());
+ size_ = n;
+ }
+
+ void set_capacity(size_type n) {
+ S2_DCHECK_LE(size(), n);
+ is_exponent_ = (n >= kExponentStart);
+ capacity_ = is_exponent_ ? Bits::Log2Ceiling(n) : n;
+ // A tiny optimization here would be to set capacity_ to kInlined if
+ // it's currently less. We don't bother, because doing so would require
+ // changing the existing comments and unittests that say that, for small n,
+ // capacity() will be exactly n if one calls reserve(n).
+ S2_DCHECK(n == capacity() || n > kInlined);
+ }
+
+ // Make capacity n or more. Reallocate and copy data as necessary.
+ void reallocate(size_type n) {
+ size_type old_capacity = capacity();
+ if (n <= old_capacity) return;
+ set_capacity(n);
+ if (MayBeInlined()) {
+ if (!IsInlined() && n <= kInlined) {
+ SetInlined();
+ return;
+ } else if (IsInlined()) {
+ if (n > kInlined) {
+ value_allocator_type allocator;
+ value_type* new_array = allocator.allocate(capacity());
+ memcpy(new_array, InlinedSpace(), size() * sizeof(T));
+ SetArray(new_array);
+ }
+ return;
+ }
+ }
+ value_allocator_type allocator;
+
+ T* new_ptr = allocator.allocate(capacity());
+ // dd: this modification fixes a ASAN/UBSAN error, because
+ // when old_capacity is 0, Array() is nullptr, which is UB
+ // for memcpy.
+ if (old_capacity > 0) {
+ memcpy(new_ptr, Array(), old_capacity * sizeof(T));
+ allocator.deallocate(Array(), old_capacity);
+ }
+
+ SetArray(new_ptr);
+ }
+
+ value_type* lastp() { return Array() + size(); }
+
+ void move(const value_type* first, const value_type* last, value_type* out) {
+ memmove(out, first, (last - first) * sizeof(value_type));
+ }
+
+ iterator make_hole(const_iterator p, size_type n) {
+ iterator q = const_cast<iterator>(p);
+ if (n != 0) {
+ size_type new_size = size() + n;
+ size_type index = q - Array();
+ reallocate(new_size);
+ q = Array() + index;
+ move(q, Array() + new_size - n, q + n);
+ set_size(new_size);
+ }
+ return q;
+ }
+
+ void erase_aux(const_iterator p, size_type n) {
+ iterator q = const_cast<iterator>(p);
+ size_type new_size = size() - n;
+ move(q + n, lastp(), q);
+ reallocate(new_size);
+ set_size(new_size);
+ }
+
+ private: // Helper functions for range/value.
+ void value_init(size_type n, const value_type& v) {
+ reserve(n);
+ set_size(n);
+ std::fill(Array(), lastp(), v);
+ }
+
+ template <typename InputIter>
+ void range_init(InputIter first, InputIter last, std::input_iterator_tag) {
+ for ( ; first != last; ++first)
+ push_back(*first);
+ }
+
+ template <typename ForwIter>
+ void range_init(ForwIter first, ForwIter last, std::forward_iterator_tag) {
+ size_type n = std::distance(first, last);
+ reserve(n);
+ set_size(n);
+ std::copy(first, last, Array());
+ }
+
+ template <typename Integer>
+ void initialize(Integer n, Integer v, std::true_type) {
+ value_init(n, v);
+ }
+
+ template <typename Iterator>
+ void initialize(Iterator first, Iterator last, std::false_type) {
+ typedef typename std::iterator_traits<Iterator>::iterator_category Cat;
+ range_init(first, last, Cat());
+ }
+
+ void value_insert(const_iterator p, size_type n, const value_type& v) {
+ if (n + size() > kMaxSize) {
+ throw std::length_error("compact_array size exceeded");
+ }
+ iterator hole = make_hole(p, n);
+ std::fill(hole, hole + n, v);
+ }
+
+ template <typename InputIter>
+ void range_insert(const_iterator p, InputIter first, InputIter last,
+ std::input_iterator_tag) {
+ size_type pos = p - begin();
+ size_type old_size = size();
+ for (; first != last; ++first)
+ push_back(*first);
+ std::rotate(begin() + pos, begin() + old_size, end());
+ }
+
+ template <typename ForwIter>
+ void range_insert(const_iterator p, ForwIter first, ForwIter last,
+ std::forward_iterator_tag) {
+ size_type n = std::distance(first, last);
+ if (n + size() > kMaxSize) {
+ throw std::length_error("compact_array size exceeded");
+ }
+ std::copy(first, last, make_hole(p, n));
+ }
+
+ template <typename Integer>
+ void insert(const_iterator p, Integer n, Integer v, std::true_type) {
+ value_insert(p, n, v);
+ }
+
+ template <typename Iterator>
+ void insert(const_iterator p, Iterator first, Iterator last,
+ std::false_type) {
+ typedef typename std::iterator_traits<Iterator>::iterator_category Cat;
+ range_insert(p, first, last, Cat());
+ }
+ static_assert(absl::is_trivially_copy_constructible<value_type>::value &&
+ absl::is_trivially_copy_assignable<value_type>::value &&
+ absl::is_trivially_destructible<value_type>::value,
+ "Requires trivial copy, assignment, and destructor.");
+};
+
+// Allocates storage for constants in compact_array_base<T>
+template <typename T, typename A>
+ const int compact_array_base<T, A>::kSizeNumBits;
+template <typename T, typename A>
+ const int compact_array_base<T, A>::kCapacityNumBits;
+template <typename T, typename A>
+ const int compact_array_base<T, A>::kMaxSize;
+template <typename T, typename A>
+ const int compact_array_base<T, A>::kExponentStart;
+
+// compact_array: Wrapper for compact_array_base that provides the
+// constructors and destructor.
+
+template <class T, class A = std::allocator<T> >
+class compact_array : public compact_array_base<T, A> {
+ private:
+ typedef compact_array_base<T, A> Base;
+
+ public:
+ typedef typename Base::value_type value_type;
+ typedef typename Base::allocator_type allocator_type;
+ typedef typename Base::pointer pointer;
+ typedef typename Base::const_pointer const_pointer;
+ typedef typename Base::reference reference;
+ typedef typename Base::const_reference const_reference;
+ typedef typename Base::size_type size_type;
+
+ typedef typename Base::iterator iterator;
+ typedef typename Base::const_iterator const_iterator;
+ typedef typename Base::reverse_iterator reverse_iterator;
+ typedef typename Base::const_reverse_iterator const_reverse_iterator;
+
+ compact_array() noexcept(noexcept(std::declval<Base&>().Init())) {
+ Base::Init();
+ }
+
+ explicit compact_array(size_type n) {
+ Base::Construct(n, value_type());
+ }
+
+ compact_array(size_type n, const value_type& v) {
+ Base::Construct(n, v);
+ }
+
+ // See 23.1.1/9 in the C++ standard for an explanation.
+ template <typename Iterator>
+ compact_array(Iterator first, Iterator last) {
+ Base::Copy(first, last);
+ }
+
+ compact_array(const compact_array& v) {
+ Base::CopyFrom(v);
+ }
+
+ compact_array(compact_array&& v) noexcept(
+ noexcept(compact_array()) && noexcept(std::declval<Base&>().swap(v)))
+ : compact_array() {
+ Base::swap(v);
+ }
+
+ compact_array& operator=(const compact_array& v) {
+ Base::AssignFrom(v);
+ return *this;
+ }
+
+ compact_array& operator=(compact_array&& v) {
+ // swap is only right here because the objects are trivially destructible
+ // and thus there are no side effects on their destructor.
+ // Otherwise we must destroy the objects on `this`.
+ Base::swap(v);
+ return *this;
+ }
+
+ ~compact_array() {
+ Base::Destruct();
+ }
+};
+
+// Comparison operators
+template <typename T, typename A>
+bool operator==(const compact_array<T, A>& x, const compact_array<T, A>& y) {
+ return x.size() == y.size() &&
+ std::equal(x.begin(), x.end(), y.begin());
+}
+
+template <typename T, typename A>
+bool operator!=(const compact_array<T, A>& x, const compact_array<T, A>& y) {
+ return !(x == y);
+}
+
+template <typename T, typename A>
+bool operator<(const compact_array<T, A>& x, const compact_array<T, A>& y) {
+ return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end());
+}
+
+template <typename T, typename A>
+bool operator>(const compact_array<T, A>& x, const compact_array<T, A>& y) {
+ return y < x;
+}
+
+template <typename T, typename A>
+bool operator<=(const compact_array<T, A>& x, const compact_array<T, A>& y) {
+ return !(y < x);
+}
+
+template <typename T, typename A>
+bool operator>=(const compact_array<T, A>& x, const compact_array<T, A>& y) {
+ return !(x < y);
+}
+
+// Swap
+template <typename T, typename A>
+inline void swap(compact_array<T, A>& x, compact_array<T, A>& y) {
+ x.swap(y);
+}
+
+namespace compact_array_internal {
+struct LogArray : public gtl::LogLegacyUpTo100 {
+ template <typename ElementT>
+ void Log(std::ostream& out, const ElementT& element) const { // NOLINT
+ out << element;
+ }
+ void Log(std::ostream& out, int8 c) const { // NOLINT
+ out << static_cast<int32>(c);
+ }
+ void Log(std::ostream& out, uint8 c) const { // NOLINT
+ out << static_cast<uint32>(c);
+ }
+
+ void LogOpening(std::ostream& out) const { out << "["; } // NOLINT
+ void LogClosing(std::ostream& out) const { out << "]"; } // NOLINT
+};
+} // namespace compact_array_internal
+
+// Output operator for compact_array<T>. Requires that T has an
+// operator<< for std::ostream. Note that
+// compact_array_internal::LogArray ensures that "signed char" and
+// "unsigned char" types print as integers.
+template <class T, class A>
+std::ostream& operator<<(std::ostream& out, const compact_array<T, A>& array) {
+ gtl::LogRangeToStream(out, array.begin(), array.end(),
+ compact_array_internal::LogArray());
+ return out;
+}
+
+} // namespace gtl
+
+#endif // S2_UTIL_GTL_COMPACT_ARRAY_H_
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// All Rights Reserved.
+//
+// Utilities for container logging.
+// TODO(user): Broaden the scope and rename to "stream_util.h"
+//
+
+#ifndef S2_UTIL_GTL_CONTAINER_LOGGING_H_
+#define S2_UTIL_GTL_CONTAINER_LOGGING_H_
+
+#include <limits>
+#include <ostream>
+#include <string>
+#include <type_traits>
+
+#include "s2/base/integral_types.h"
+#include "s2/base/port.h"
+#include "s2/strings/ostringstream.h"
+
+namespace gtl {
+
+// Several policy classes below determine how LogRangeToStream will
+// format a range of items. A Policy class should have these methods:
+//
+// Called to print an individual container element.
+// void Log(ostream &out, const ElementT &element) const;
+//
+// Called before printing the set of elements:
+// void LogOpening(ostream &out) const;
+//
+// Called after printing the set of elements:
+// void LogClosing(ostream &out) const;
+//
+// Called before printing the first element:
+// void LogFirstSeparator(ostream &out) const;
+//
+// Called before printing the remaining elements:
+// void LogSeparator(ostream &out) const;
+//
+// Returns the maximum number of elements to print:
+// int64 MaxElements() const;
+//
+// Called to print an indication that MaximumElements() was reached:
+// void LogEllipsis(ostream &out) const;
+
+namespace internal {
+
+struct LogBase {
+ template <typename ElementT>
+ void Log(std::ostream &out, const ElementT &element) const { // NOLINT
+ out << element;
+ }
+ void LogEllipsis(std::ostream &out) const { // NOLINT
+ out << "...";
+ }
+};
+
+struct LogShortBase : public LogBase {
+ void LogOpening(std::ostream &out) const { out << "["; } // NOLINT
+ void LogClosing(std::ostream &out) const { out << "]"; } // NOLINT
+ void LogFirstSeparator(std::ostream &out) const { out << ""; } // NOLINT
+ void LogSeparator(std::ostream &out) const { out << ", "; } // NOLINT
+};
+
+struct LogMultilineBase : public LogBase {
+ void LogOpening(std::ostream &out) const { out << "["; } // NOLINT
+ void LogClosing(std::ostream &out) const { out << "\n]"; } // NOLINT
+ void LogFirstSeparator(std::ostream &out) const { out << "\n"; } // NOLINT
+ void LogSeparator(std::ostream &out) const { out << "\n"; } // NOLINT
+};
+
+struct LogLegacyBase : public LogBase {
+ void LogOpening(std::ostream &out) const { out << ""; } // NOLINT
+ void LogClosing(std::ostream &out) const { out << ""; } // NOLINT
+ void LogFirstSeparator(std::ostream &out) const { out << ""; } // NOLINT
+ void LogSeparator(std::ostream &out) const { out << " "; } // NOLINT
+};
+
+} // namespace internal
+
+// LogShort uses [] braces and separates items with comma-spaces. For
+// example "[1, 2, 3]".
+struct LogShort : public internal::LogShortBase {
+ int64 MaxElements() const { return std::numeric_limits<int64>::max(); }
+};
+
+// LogShortUpToN(max_elements) formats the same as LogShort but prints no more
+// than the max_elements elements.
+class LogShortUpToN : public internal::LogShortBase {
+ public:
+ explicit LogShortUpToN(int64 max_elements) : max_elements_(max_elements) {}
+ int64 MaxElements() const { return max_elements_; }
+
+ private:
+ int64 max_elements_;
+};
+
+// LogShortUpTo100 formats the same as LogShort but prints no more
+// than 100 elements.
+struct LogShortUpTo100 : public LogShortUpToN {
+ LogShortUpTo100() : LogShortUpToN(100) {}
+};
+
+// LogMultiline uses [] braces and separates items with
+// newlines. For example "[
+// 1
+// 2
+// 3
+// ]".
+struct LogMultiline : public internal::LogMultilineBase {
+ int64 MaxElements() const { return std::numeric_limits<int64>::max(); }
+};
+
+// LogMultilineUpToN(max_elements) formats the same as LogMultiline but
+// prints no more than max_elements elements.
+class LogMultilineUpToN : public internal::LogMultilineBase {
+ public:
+ explicit LogMultilineUpToN(int64 max_elements)
+ : max_elements_(max_elements) {}
+ int64 MaxElements() const { return max_elements_; }
+
+ private:
+ int64 max_elements_;
+};
+
+// LogMultilineUpTo100 formats the same as LogMultiline but
+// prints no more than 100 elements.
+struct LogMultilineUpTo100 : public LogMultilineUpToN {
+ LogMultilineUpTo100() : LogMultilineUpToN(100) {}
+};
+
+// The legacy behavior of LogSequence() does not use braces and
+// separates items with spaces. For example "1 2 3".
+struct LogLegacyUpTo100 : public internal::LogLegacyBase {
+ int64 MaxElements() const { return 100; }
+};
+struct LogLegacy : public internal::LogLegacyBase {
+ int64 MaxElements() const { return std::numeric_limits<int64>::max(); }
+};
+
+// The default policy for new code.
+typedef LogShortUpTo100 LogDefault;
+
+// LogRangeToStream should be used to define operator<< for
+// STL and STL-like containers. For example, see stl_logging.h.
+template <typename IteratorT, typename PolicyT>
+inline void LogRangeToStream(std::ostream &out, // NOLINT
+ IteratorT begin, IteratorT end,
+ const PolicyT &policy) {
+ policy.LogOpening(out);
+ for (size_t i = 0; begin != end && i < policy.MaxElements(); ++i, ++begin) {
+ if (i == 0) {
+ policy.LogFirstSeparator(out);
+ } else {
+ policy.LogSeparator(out);
+ }
+ policy.Log(out, *begin);
+ }
+ if (begin != end) {
+ policy.LogSeparator(out);
+ policy.LogEllipsis(out);
+ }
+ policy.LogClosing(out);
+}
+
+namespace detail {
+
+// RangeLogger is a helper class for gtl::LogRange and
+// gtl::LogContainer; do not use it directly. This object
+// captures iterators into the argument of the LogRange and
+// LogContainer functions, so its lifetime should be confined to a
+// single logging statement. Objects of this type should not be
+// assigned to local variables.
+template <typename IteratorT, typename PolicyT>
+class RangeLogger {
+ public:
+ RangeLogger(const IteratorT &begin, const IteratorT &end,
+ const PolicyT &policy)
+ : begin_(begin), end_(end), policy_(policy) { }
+
+ friend std::ostream &operator<<(std::ostream &out, const RangeLogger &range) {
+ gtl::LogRangeToStream<IteratorT, PolicyT>(out, range.begin_, range.end_,
+ range.policy_);
+ return out;
+ }
+
+ // operator<< above is generally recommended. However, some situations may
+ // require a string, so a convenience str() method is provided as well.
+ string str() const {
+ string s;
+ ::strings::OStringStream(&s) << *this;
+ return s;
+ }
+
+ private:
+ IteratorT begin_;
+ IteratorT end_;
+ PolicyT policy_;
+};
+
+template <typename E>
+class EnumLogger {
+ public:
+ explicit EnumLogger(E e) : e_(e) {}
+
+ friend std::ostream &operator<<(std::ostream &out, const EnumLogger &v) {
+ using I = typename std::underlying_type<E>::type;
+ return out << static_cast<I>(v.e_);
+ }
+
+ private:
+ E e_;
+};
+
+} // namespace detail
+
+// Log a range using "policy". For example:
+//
+// S2_LOG(INFO) << gtl::LogRange(start_pos, end_pos, gtl::LogMultiline());
+//
+// The above example will print the range using newlines between
+// elements, enclosed in [] braces.
+template <typename IteratorT, typename PolicyT>
+detail::RangeLogger<IteratorT, PolicyT> LogRange(
+ const IteratorT &begin, const IteratorT &end, const PolicyT &policy) {
+ return gtl::detail::RangeLogger<IteratorT, PolicyT>(begin, end, policy);
+}
+
+// Log a range. For example:
+//
+// S2_LOG(INFO) << gtl::LogRange(start_pos, end_pos);
+//
+// By default, Range() uses the LogShortUpTo100 policy: comma-space
+// separation, no newlines, and with limit of 100 items.
+template <typename IteratorT>
+detail::RangeLogger<IteratorT, LogDefault> LogRange(
+ const IteratorT &begin, const IteratorT &end) {
+ return gtl::LogRange(begin, end, LogDefault());
+}
+
+// Log a container using "policy". For example:
+//
+// S2_LOG(INFO) << gtl::LogContainer(container, gtl::LogMultiline());
+//
+// The above example will print the container using newlines between
+// elements, enclosed in [] braces.
+template <typename ContainerT, typename PolicyT>
+auto LogContainer(const ContainerT &container, const PolicyT &policy)
+ -> decltype(gtl::LogRange(container.begin(), container.end(), policy)) {
+ return gtl::LogRange(container.begin(), container.end(), policy);
+}
+
+// Log a container. For example:
+//
+// S2_LOG(INFO) << gtl::LogContainer(container);
+//
+// By default, Container() uses the LogShortUpTo100 policy: comma-space
+// separation, no newlines, and with limit of 100 items.
+template <typename ContainerT>
+auto LogContainer(const ContainerT &container)
+ -> decltype(gtl::LogContainer(container, LogDefault())) {
+ return gtl::LogContainer(container, LogDefault());
+}
+
+// Log a (possibly scoped) enum. For example:
+//
+// enum class Color { kRed, kGreen, kBlue };
+// S2_LOG(INFO) << gtl::LogEnum(kRed);
+template <typename E>
+detail::EnumLogger<E> LogEnum(E e) {
+ static_assert(std::is_enum<E>::value, "must be an enum");
+ return detail::EnumLogger<E>(e);
+}
+
+} // namespace gtl
+
+#endif // S2_UTIL_GTL_CONTAINER_LOGGING_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// ---
+//
+// This is just a very thin wrapper over densehashtable.h, just
+// like sgi stl's stl_hash_set is a very thin wrapper over
+// stl_hashtable.
+//
+// This is more different from dense_hash_map than you might think,
+// because all iterators for sets are const (you obviously can't
+// change the key, and for sets there is no value).
+//
+// NOTE: this is exactly like sparse_hash_set.h, with the word
+// "sparse" replaced by "dense", except for the addition of
+// set_empty_key().
+//
+// YOU MUST CALL SET_EMPTY_KEY() IMMEDIATELY AFTER CONSTRUCTION.
+//
+// Otherwise your program will die in mysterious ways. (Note if you
+// use the constructor that takes an InputIterator range, you pass in
+// the empty key in the constructor, rather than after. As a result,
+// this constructor differs from the standard STL version.)
+//
+// In other respects, we adhere mostly to the STL semantics for
+// hash-map. One important exception is that insert() may invalidate
+// iterators entirely -- STL semantics are that insert() may reorder
+// iterators, but they all still refer to something valid in the
+// hashtable. Not so for us. Likewise, insert() may invalidate
+// pointers into the hashtable. (Whether insert invalidates iterators
+// and pointers depends on whether it results in a hashtable resize,
+// but that's an implementation detail that may change in the future.)
+// On the plus side, delete() doesn't invalidate iterators or pointers
+// at all, or even change the ordering of elements.
+//
+// Also please note:
+//
+// 1) set_deleted_key():
+// If you want to use erase() you must call set_deleted_key(),
+// in addition to set_empty_key(), after construction.
+// The deleted and empty keys must differ.
+//
+// 2) Keys equal to the empty key or deleted key (if any) cannot be
+// used as keys for find(), count(), insert(), etc.
+//
+// 3) min_load_factor():
+// Setting the minimum load factor controls how aggressively the
+// table is shrunk when keys are erased. Setting it to 0.0
+// guarantees that the hash table will never shrink.
+//
+// 4) resize(0):
+// When an item is deleted, its memory isn't freed right
+// away. This allows you to iterate over a hashtable,
+// and call erase(), without invalidating the iterator.
+// To force the memory to be freed, call resize(0).
+// For tr1 compatibility, this can also be called as rehash(0).
+// Roughly speaking:
+// (1) dense_hash_set: fastest, uses the most memory unless entries are small
+// (2) sparse_hash_set: slowest, uses the least memory
+// (3) hash_set / unordered_set (STL): in the middle
+//
+// Typically I use sparse_hash_set when I care about space and/or when
+// I need to save the hashtable on disk. I use hash_set otherwise. I
+// don't personally use dense_hash_set ever; some people use it for
+// small sets with lots of lookups.
+//
+// - dense_hash_set has, typically, about 78% memory overhead (if your
+// data takes up X bytes, the hash_set uses .78X more bytes in overhead).
+// - sparse_hash_set has about 4 bits overhead per entry.
+// - sparse_hash_set can be 3-7 times slower than the others for lookup and,
+// especially, inserts. See time_hash_map.cc for details.
+//
+// See /usr/(local/)?doc/sparsehash-*/dense_hash_set.html
+// for information about how to use this class.
+
+#ifndef S2_UTIL_GTL_DENSE_HASH_SET_H_
+#define S2_UTIL_GTL_DENSE_HASH_SET_H_
+
+#include <cstdio>
+#include <algorithm>
+#include <functional>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "s2/base/port.h"
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/util/gtl/densehashtable.h" // IWYU pragma: export
+
+// Some files test for this symbol.
+#define S2__DENSE_HASH_SET_H_
+
+namespace gtl {
+
+template <class Value,
+ class HashFcn = std::hash<Value>,
+ class EqualKey = std::equal_to<Value>,
+ class Alloc = std::allocator<Value> >
+class dense_hash_set {
+ private:
+
+ // Apparently identity is not stl-standard, so we define our own
+ struct Identity {
+ typedef const Value& result_type;
+ const Value& operator()(const Value& v) const { return v; }
+ };
+ struct SetKey {
+ void operator()(Value* value, const Value& new_key) const {
+ *value = new_key;
+ }
+ };
+
+ // The actual data
+ typedef dense_hashtable<Value, Value, HashFcn, Identity, SetKey,
+ EqualKey, Alloc> ht;
+ ht rep;
+
+ public:
+ typedef typename ht::key_type key_type;
+ typedef typename ht::value_type value_type;
+ typedef typename ht::hasher hasher;
+ typedef typename ht::key_equal key_equal;
+ typedef Alloc allocator_type;
+
+ typedef typename ht::size_type size_type;
+ typedef typename ht::difference_type difference_type;
+ typedef typename ht::const_pointer pointer;
+ typedef typename ht::const_pointer const_pointer;
+ typedef typename ht::const_reference reference;
+ typedef typename ht::const_reference const_reference;
+
+ typedef typename ht::const_iterator iterator;
+ typedef typename ht::const_iterator const_iterator;
+ typedef typename ht::const_local_iterator local_iterator;
+ typedef typename ht::const_local_iterator const_local_iterator;
+
+
+ // Iterator functions -- recall all iterators are const
+ iterator begin() const { return rep.begin(); }
+ iterator end() const { return rep.end(); }
+
+ // These come from tr1's unordered_set. For us, a bucket has 0 or 1 elements.
+ ABSL_DEPRECATED(
+ "This method is slated for removal. Please migrate to "
+ "absl::flat_hash_set.")
+ local_iterator begin(size_type i) const { return rep.begin(i); }
+
+ ABSL_DEPRECATED(
+ "This method is slated for removal. Please migrate to "
+ "absl::flat_hash_set.")
+ local_iterator end(size_type i) const { return rep.end(i); }
+
+
+ // Accessor functions
+ allocator_type get_allocator() const { return rep.get_allocator(); }
+ hasher hash_funct() const { return rep.hash_funct(); }
+ hasher hash_function() const { return hash_funct(); } // tr1 name
+ key_equal key_eq() const { return rep.key_eq(); }
+
+
+ // Constructors
+ dense_hash_set() {}
+
+ explicit dense_hash_set(size_type expected_max_items_in_table,
+ const hasher& hf = hasher(),
+ const key_equal& eql = key_equal(),
+ const allocator_type& alloc = allocator_type())
+ : rep(expected_max_items_in_table, hf, eql, Identity(), SetKey(), alloc) {
+ }
+
+ template <class InputIterator>
+ dense_hash_set(InputIterator f, InputIterator l,
+ const key_type& empty_key_val,
+ size_type expected_max_items_in_table = 0,
+ const hasher& hf = hasher(),
+ const key_equal& eql = key_equal(),
+ const allocator_type& alloc = allocator_type())
+ : rep(expected_max_items_in_table, hf, eql, Identity(), SetKey(), alloc) {
+ set_empty_key(empty_key_val);
+ rep.insert(f, l);
+ }
+ // We use the default copy constructor
+ // We use the default operator=()
+ // We use the default destructor
+
+ void clear() { rep.clear(); }
+ // This clears the hash set without resizing it down to the minimum
+ // bucket count, but rather keeps the number of buckets constant
+ void clear_no_resize() { rep.clear_no_resize(); }
+ void swap(dense_hash_set& hs) { rep.swap(hs.rep); }
+
+
+ // Functions concerning size
+ size_type size() const { return rep.size(); }
+ size_type max_size() const { return rep.max_size(); }
+ bool empty() const { return rep.empty(); }
+ size_type bucket_count() const { return rep.bucket_count(); }
+
+ ABSL_DEPRECATED(
+ "This method is slated for removal. Please migrate to "
+ "absl::flat_hash_set.")
+ size_type max_bucket_count() const { return rep.max_bucket_count(); }
+
+ // These are tr1 methods. bucket() is the bucket the key is or would be in.
+ ABSL_DEPRECATED(
+ "This method is slated for removal. Please migrate to "
+ "absl::flat_hash_set.")
+ size_type bucket_size(size_type i) const { return rep.bucket_size(i); }
+ ABSL_DEPRECATED(
+ "This method is slated for removal. Please migrate to "
+ "absl::flat_hash_set.")
+ size_type bucket(const key_type& key) const { return rep.bucket(key); }
+ float load_factor() const {
+ return size() * 1.0f / bucket_count();
+ }
+ float max_load_factor() const {
+ float shrink, grow;
+ rep.get_resizing_parameters(&shrink, &grow);
+ return grow;
+ }
+ void max_load_factor(float new_grow) {
+ float shrink, grow;
+ rep.get_resizing_parameters(&shrink, &grow);
+ rep.set_resizing_parameters(shrink, new_grow);
+ }
+ // These aren't tr1 methods but perhaps ought to be.
+ ABSL_DEPRECATED(
+ "This method is slated for removal. Please migrate to "
+ "absl::flat_hash_set.")
+ float min_load_factor() const {
+ float shrink, grow;
+ rep.get_resizing_parameters(&shrink, &grow);
+ return shrink;
+ }
+ void min_load_factor(float new_shrink) {
+ float shrink, grow;
+ rep.get_resizing_parameters(&shrink, &grow);
+ rep.set_resizing_parameters(new_shrink, grow);
+ }
+ // Deprecated; use min_load_factor() or max_load_factor() instead.
+ void set_resizing_parameters(float shrink, float grow) {
+ rep.set_resizing_parameters(shrink, grow);
+ }
+
+ void resize(size_type hint) { rep.resize(hint); }
+ void rehash(size_type hint) { resize(hint); } // the tr1 name
+
+ // Lookup routines
+ iterator find(const key_type& key) const { return rep.find(key); }
+ size_type count(const key_type& key) const { return rep.count(key); }
+ std::pair<iterator, iterator> equal_range(const key_type& key) const {
+ return rep.equal_range(key);
+ }
+
+
+ // Insertion routines
+ std::pair<iterator, bool> insert(const value_type& obj) {
+ std::pair<typename ht::iterator, bool> p = rep.insert(obj);
+ return std::pair<iterator, bool>(p.first, p.second); // const to non-const
+ }
+ std::pair<iterator, bool> insert(value_type&& obj) { // NOLINT
+ std::pair<typename ht::iterator, bool> p = rep.insert(std::move(obj));
+ return std::pair<iterator, bool>(p.first, p.second); // const to non-const
+ }
+ template <class InputIterator> void insert(InputIterator f, InputIterator l) {
+ rep.insert(f, l);
+ }
+ void insert(const_iterator f, const_iterator l) {
+ rep.insert(f, l);
+ }
+ // Required for std::insert_iterator; the passed-in iterator is ignored.
+ iterator insert(iterator, const value_type& obj) {
+ return insert(obj).first;
+ }
+ iterator insert(iterator, value_type&& obj) { // NOLINT
+ return insert(std::move(obj)).first;
+ }
+
+ // Unlike std::set, we cannot construct an element in place, as we do not have
+ // a layer of indirection like std::set nodes. Therefore, emplace* methods do
+ // not provide a performance advantage over insert + move.
+ template <typename... Args>
+ std::pair<iterator, bool> emplace(Args&&... args) {
+ return rep.insert(value_type(std::forward<Args>(args)...));
+ }
+ // The passed-in const_iterator is ignored.
+ template <typename... Args>
+ iterator emplace_hint(const_iterator, Args&&... args) {
+ return rep.insert(value_type(std::forward<Args>(args)...)).first;
+ }
+
+ // Deletion and empty routines
+ // THESE ARE NON-STANDARD! I make you specify an "impossible" key
+ // value to identify deleted and empty buckets. You can change the
+ // deleted key as time goes on, or get rid of it entirely to be insert-only.
+ void set_empty_key(const key_type& key) { rep.set_empty_key(key); }
+ void set_deleted_key(const key_type& key) { rep.set_deleted_key(key); }
+
+ // These are standard
+ size_type erase(const key_type& key) { return rep.erase(key); }
+ void erase(iterator it) { rep.erase(it); }
+ void erase(iterator f, iterator l) { rep.erase(f, l); }
+
+
+ // Comparison
+ bool operator==(const dense_hash_set& hs) const { return rep == hs.rep; }
+ bool operator!=(const dense_hash_set& hs) const { return rep != hs.rep; }
+};
+
+template <class Val, class HashFcn, class EqualKey, class Alloc>
+inline void swap(dense_hash_set<Val, HashFcn, EqualKey, Alloc>& hs1,
+ dense_hash_set<Val, HashFcn, EqualKey, Alloc>& hs2) {
+ hs1.swap(hs2);
+}
+
+}
+
+#endif // S2_UTIL_GTL_DENSE_HASH_SET_H_
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// ---
+//
+// A dense hashtable is a particular implementation of
+// a hashtable: one that is meant to minimize memory allocation.
+// It does this by using an array to store all the data. We
+// steal a value from the key space to indicate "empty" array
+// elements (ie indices where no item lives) and another to indicate
+// "deleted" elements.
+//
+// (Note it is possible to change the value of the delete key
+// on the fly; you can even remove it, though after that point
+// the hashtable is insert_only until you set it again. The empty
+// value however can't be changed.)
+//
+// To minimize allocation and pointer overhead, we use internal
+// probing, in which the hashtable is a single table, and collisions
+// are resolved by trying to insert again in another bucket. The
+// most cache-efficient internal probing schemes are linear probing
+// (which suffers, alas, from clumping) and quadratic probing, which
+// is what we implement by default.
+//
+// Type requirements: value_type is required to be Move Constructible
+// and Default Constructible. It is not required to be (and commonly
+// isn't) Assignable.
+//
+// You probably shouldn't use this code directly. Use dense_hash_map<>
+// or dense_hash_set<> instead.
+
+// You can change the following below:
+// HT_OCCUPANCY_PCT -- how full before we double size
+// HT_EMPTY_PCT -- how empty before we halve size
+// HT_MIN_BUCKETS -- default smallest bucket size
+//
+// You can also change enlarge_factor (which defaults to
+// HT_OCCUPANCY_PCT), and shrink_factor (which defaults to
+// HT_EMPTY_PCT) with set_resizing_parameters().
+//
+// How to decide what values to use?
+// shrink_factor's default of .4 * OCCUPANCY_PCT, is probably good.
+// HT_MIN_BUCKETS is probably unnecessary since you can specify
+// (indirectly) the starting number of buckets at construct-time.
+// For enlarge_factor, you can use this chart to try to trade-off
+// expected lookup time to the space taken up. By default, this
+// code uses quadratic probing, though you can change it to linear
+// via JUMP_ below if you really want to.
+//
+// From
+// L = N / M,
+// where N is the number of data items in the table and M is the table size.
+// NUMBER OF PROBES / LOOKUP Successful Unsuccessful
+// Quadratic collision resolution 1 - ln(1-L) - L/2 1/(1-L) - L - ln(1-L)
+// Linear collision resolution [1+1/(1-L)]/2 [1+1/(1-L)^2]/2
+//
+// -- enlarge_factor -- 0.10 0.50 0.60 0.75 0.80 0.90 0.99
+// QUADRATIC COLLISION RES.
+// probes/successful lookup 1.05 1.44 1.62 2.01 2.21 2.85 5.11
+// probes/unsuccessful lookup 1.11 2.19 2.82 4.64 5.81 11.4 103.6
+// LINEAR COLLISION RES.
+// probes/successful lookup 1.06 1.5 1.75 2.5 3.0 5.5 50.5
+// probes/unsuccessful lookup 1.12 2.5 3.6 8.5 13.0 50.0 5000.0
+
+#ifndef S2_UTIL_GTL_DENSEHASHTABLE_H_
+#define S2_UTIL_GTL_DENSEHASHTABLE_H_
+
+#include <cassert>
+#include <cstddef>
+#include <cstdio> // for FILE, fwrite, fread
+#include <algorithm> // For swap(), eg
+#include <functional>
+#include <iterator> // For iterator tags
+#include <limits> // for numeric_limits
+#include <memory> // For uninitialized_fill
+#include <new>
+#include <string>
+#include <utility>
+#include <vector>
+#include <type_traits>
+
+#include "s2/util/gtl/hashtable_common.h"
+#include "s2/base/port.h"
+#include <stdexcept> // For length_error
+
+namespace gtl {
+
+
+// Some files test for this symbol.
+#define S2__DENSEHASHTABLE_H_
+
+// The probing method
+// Linear probing
+// #define JUMP_(key, num_probes) ( 1 )
+// Quadratic probing
+#define JUMP_(key, num_probes) (num_probes)
+
+// The weird mod in the offset is entirely to quiet compiler warnings
+// as is the cast to int after doing the "x mod 256"
+#define PUT_(take_from, offset) do { \
+ if (putc(static_cast<int>(offset >= sizeof(take_from)*8) \
+ ? 0 : ((take_from) >> (offset)) % 256, fp) \
+ == EOF) \
+ return false; \
+} while (0)
+
+#define GET_(add_to, offset) do { \
+ if ((x=getc(fp)) == EOF) \
+ return false; \
+ else if (offset >= sizeof(add_to) * 8) \
+ assert(x == 0); /* otherwise it's too big for us to represent */ \
+ else \
+ add_to |= (static_cast<size_type>(x) << ((offset) % (sizeof(add_to)*8))); \
+} while (0)
+
+
+// Hashtable class, used to implement the hashed associative containers
+// hash_set and hash_map.
+
+// Value: what is stored in the table (each bucket is a Value).
+// Key: something in a 1-to-1 correspondence to a Value, that can be used
+// to search for a Value in the table (find() takes a Key).
+// HashFcn: Takes a Key and returns an integer, the more unique the better.
+// ExtractKey: given a Value, returns the unique Key associated with it.
+// Must have a result_type enum indicating the return type of
+// operator().
+// SetKey: given a Value* and a Key, modifies the value such that
+// ExtractKey(value) == key. We guarantee this is only called
+// with key == deleted_key or key == empty_key.
+// EqualKey: Given two Keys, says whether they are the same (that is,
+// if they are both associated with the same Value).
+// Alloc: STL allocator to use to allocate memory.
+
+template <class Value, class Key, class HashFcn,
+ class ExtractKey, class SetKey, class EqualKey, class Alloc>
+class dense_hashtable;
+
+template <class V, class K, class HF, class ExK, class SetK, class EqK, class A>
+struct dense_hashtable_const_iterator;
+
+// We're just an array, but we need to skip over empty and deleted elements
+template <class V, class K, class HF, class ExK, class SetK, class EqK, class A>
+struct dense_hashtable_iterator {
+ private:
+ typedef typename A::template rebind<V>::other value_alloc_type;
+
+ public:
+ typedef dense_hashtable_iterator<V, K, HF, ExK, SetK, EqK, A>
+ iterator;
+ typedef dense_hashtable_const_iterator<V, K, HF, ExK, SetK, EqK, A>
+ const_iterator;
+
+ typedef std::forward_iterator_tag iterator_category; // very little defined!
+ typedef V value_type;
+ typedef typename value_alloc_type::difference_type difference_type;
+ typedef typename value_alloc_type::size_type size_type;
+ typedef typename value_alloc_type::reference reference;
+ typedef typename value_alloc_type::pointer pointer;
+
+ // "Real" constructor and default constructor
+ dense_hashtable_iterator(
+ const dense_hashtable<V, K, HF, ExK, SetK, EqK, A> *h,
+ pointer it, pointer it_end, bool advance)
+ : ht(h), pos(it), end(it_end) {
+ if (advance) advance_past_empty_and_deleted();
+ }
+ dense_hashtable_iterator() { }
+ // The default destructor is fine; we don't define one
+ // The default operator= is fine; we don't define one
+
+ // Happy dereferencer
+ reference operator*() const { return *pos; }
+ pointer operator->() const { return &(operator*()); }
+
+ // Arithmetic. The only hard part is making sure that
+ // we're not on an empty or marked-deleted array element
+ void advance_past_empty_and_deleted() {
+ while (pos != end && (ht->test_empty(*this) || ht->test_deleted(*this)) )
+ ++pos;
+ }
+ iterator& operator++() {
+ assert(pos != end);
+ ++pos;
+ advance_past_empty_and_deleted();
+ return *this;
+ }
+ iterator operator++(int /*unused*/) {
+ auto tmp(*this);
+ ++*this;
+ return tmp;
+ }
+
+ // Comparison.
+ bool operator==(const iterator& it) const { return pos == it.pos; }
+ bool operator!=(const iterator& it) const { return pos != it.pos; }
+
+
+ // The actual data
+ const dense_hashtable<V, K, HF, ExK, SetK, EqK, A> *ht;
+ pointer pos, end;
+};
+
+
+// Now do it all again, but with const-ness!
+template <class V, class K, class HF, class ExK, class SetK, class EqK, class A>
+struct dense_hashtable_const_iterator {
+ private:
+ typedef typename A::template rebind<V>::other value_alloc_type;
+
+ public:
+ typedef dense_hashtable_iterator<V, K, HF, ExK, SetK, EqK, A>
+ iterator;
+ typedef dense_hashtable_const_iterator<V, K, HF, ExK, SetK, EqK, A>
+ const_iterator;
+
+ typedef std::forward_iterator_tag iterator_category; // very little defined!
+ typedef V value_type;
+ typedef typename value_alloc_type::difference_type difference_type;
+ typedef typename value_alloc_type::size_type size_type;
+ typedef typename value_alloc_type::const_reference reference;
+ typedef typename value_alloc_type::const_pointer pointer;
+
+ // "Real" constructor and default constructor
+ dense_hashtable_const_iterator(
+ const dense_hashtable<V, K, HF, ExK, SetK, EqK, A> *h,
+ pointer it, pointer it_end, bool advance)
+ : ht(h), pos(it), end(it_end) {
+ if (advance) advance_past_empty_and_deleted();
+ }
+ dense_hashtable_const_iterator()
+ : ht(nullptr), pos(pointer()), end(pointer()) { }
+ // This lets us convert regular iterators to const iterators
+ dense_hashtable_const_iterator(const iterator &it)
+ : ht(it.ht), pos(it.pos), end(it.end) { }
+ // The default destructor is fine; we don't define one
+ // The default operator= is fine; we don't define one
+
+ // Happy dereferencer
+ reference operator*() const { return *pos; }
+ pointer operator->() const { return &(operator*()); }
+
+ // Arithmetic. The only hard part is making sure that
+ // we're not on an empty or marked-deleted array element
+ void advance_past_empty_and_deleted() {
+ while (pos != end && (ht->test_empty(*this) || ht->test_deleted(*this)))
+ ++pos;
+ }
+ const_iterator& operator++() {
+ assert(pos != end);
+ ++pos;
+ advance_past_empty_and_deleted();
+ return *this;
+ }
+ const_iterator operator++(int /*unused*/) {
+ auto tmp(*this);
+ ++*this;
+ return tmp;
+ }
+
+ // Comparison.
+ bool operator==(const const_iterator& it) const { return pos == it.pos; }
+ bool operator!=(const const_iterator& it) const { return pos != it.pos; }
+
+
+ // The actual data
+ const dense_hashtable<V, K, HF, ExK, SetK, EqK, A> *ht;
+ pointer pos, end;
+};
+
+template <class Value, class Key, class HashFcn,
+ class ExtractKey, class SetKey, class EqualKey, class Alloc>
+class dense_hashtable {
+ private:
+ typedef typename Alloc::template rebind<Value>::other value_alloc_type;
+
+
+ public:
+ typedef Key key_type;
+ typedef Value value_type;
+ typedef HashFcn hasher;
+ typedef EqualKey key_equal;
+ typedef Alloc allocator_type;
+
+ typedef typename value_alloc_type::size_type size_type;
+ typedef typename value_alloc_type::difference_type difference_type;
+ typedef typename value_alloc_type::reference reference;
+ typedef typename value_alloc_type::const_reference const_reference;
+ typedef typename value_alloc_type::pointer pointer;
+ typedef typename value_alloc_type::const_pointer const_pointer;
+ typedef dense_hashtable_iterator<Value, Key, HashFcn,
+ ExtractKey, SetKey, EqualKey, Alloc>
+ iterator;
+
+ typedef dense_hashtable_const_iterator<Value, Key, HashFcn,
+ ExtractKey, SetKey, EqualKey, Alloc>
+ const_iterator;
+
+ // These come from tr1. For us they're the same as regular iterators.
+ typedef iterator local_iterator;
+ typedef const_iterator const_local_iterator;
+
+
+ // How full we let the table get before we resize, by default.
+ // Knuth says .8 is good -- higher causes us to probe too much,
+ // though it saves memory.
+ static const int HT_OCCUPANCY_PCT; // defined at the bottom of this file
+
+ // How empty we let the table get before we resize lower, by default.
+ // (0.0 means never resize lower.)
+ // It should be less than OCCUPANCY_PCT / 2 or we thrash resizing
+ static const int HT_EMPTY_PCT; // defined at the bottom of this file
+
+ // Minimum size we're willing to let hashtables be.
+ // Must be a power of two, and at least 4.
+ // Note, however, that for a given hashtable, the initial size is a
+ // function of the first constructor arg, and may be >HT_MIN_BUCKETS.
+ static const size_type HT_MIN_BUCKETS = 4;
+
+ // By default, if you don't specify a hashtable size at
+ // construction-time, we use this size. Must be a power of two, and
+ // at least HT_MIN_BUCKETS.
+ static const size_type HT_DEFAULT_STARTING_BUCKETS = 32;
+
+ // ITERATOR FUNCTIONS
+ iterator begin() {
+ return iterator(this, table, table + num_buckets, true);
+ }
+ iterator end() {
+ return iterator(this, table + num_buckets, table + num_buckets, true);
+ }
+ const_iterator begin() const {
+ return const_iterator(this, table, table+num_buckets, true);
+ }
+ const_iterator end() const {
+ return const_iterator(this, table + num_buckets, table+num_buckets, true);
+ }
+
+ // These come from tr1 unordered_map. They iterate over 'bucket' n.
+ // We'll just consider bucket n to be the n-th element of the table.
+ local_iterator begin(size_type i) {
+ return local_iterator(this, table + i, table + i+1, false);
+ }
+ local_iterator end(size_type i) {
+ local_iterator it = begin(i);
+ if (!test_empty(i) && !test_deleted(i))
+ ++it;
+ return it;
+ }
+ const_local_iterator begin(size_type i) const {
+ return const_local_iterator(this, table + i, table + i+1, false);
+ }
+ const_local_iterator end(size_type i) const {
+ const_local_iterator it = begin(i);
+ if (!test_empty(i) && !test_deleted(i))
+ ++it;
+ return it;
+ }
+
+ // ACCESSOR FUNCTIONS for the things we templatize on, basically
+ hasher hash_funct() const { return settings; }
+ key_equal key_eq() const { return key_info; }
+ value_alloc_type get_allocator() const { return key_info; }
+
+ // Accessor function for statistics gathering.
+ int num_table_copies() const { return settings.num_ht_copies(); }
+
+ private:
+ // Annoyingly, we can't copy values around, because they might have
+ // const components (they're probably std::pair<const X, Y>). We use
+ // explicit destructor invocation and placement new to get around
+ // this. Arg.
+ static void set_value(pointer dst, const_reference src) {
+ dst->~value_type(); // delete the old value, if any
+ new(dst) value_type(src);
+ }
+
+ static void set_value(pointer dst, value_type&& src) { // NOLINT
+ dst->~value_type();
+ new(dst) value_type(std::move(src));
+ }
+
+ void destroy_buckets(size_type first, size_type last) {
+ for ( ; first != last; ++first)
+ table[first].~value_type();
+ }
+
+ // DELETE HELPER FUNCTIONS
+ // This lets the user describe a key that will indicate deleted
+ // table entries. This key should be an "impossible" entry --
+ // if you try to insert it for real, you won't be able to retrieve it!
+ // (NB: while you pass in an entire value, only the key part is looked
+ // at. This is just because I don't know how to assign just a key.)
+ private:
+ // Gets rid of any deleted entries we have.
+ void squash_deleted() {
+ if (num_deleted > 0) {
+ rebucket(settings.min_buckets(size(), num_buckets));
+ }
+ assert(num_deleted == 0);
+ }
+
+ // Test if the given key is the deleted indicator. Requires
+ // num_deleted > 0, for correctness of read(), and because that
+ // guarantees that key_info.delkey is valid.
+ bool test_deleted_key(const key_type& key) const {
+ assert(num_deleted > 0);
+ return equals(key_info.delkey, key);
+ }
+
+ public:
+ void set_deleted_key(const key_type &key) {
+ // the empty indicator (if specified) and the deleted indicator
+ // must be different
+ assert((!settings.use_empty() || !equals(key, key_info.empty)) &&
+ "Passed the empty-key to set_deleted_key");
+ // It's only safe to change what "deleted" means if we purge deleted guys
+ squash_deleted();
+ settings.set_use_deleted(true);
+ key_info.delkey = key;
+ }
+ key_type deleted_key() const {
+ assert(settings.use_deleted()
+ && "Must set deleted key before calling deleted_key");
+ return key_info.delkey;
+ }
+
+ // These are public so the iterators can use them
+ // True if the item at position bucknum is "deleted" marker
+ bool test_deleted(size_type bucknum) const {
+ // Invariant: !use_deleted() implies num_deleted is 0.
+ assert(settings.use_deleted() || num_deleted == 0);
+ return num_deleted > 0 && test_deleted_key(get_key(table[bucknum]));
+ }
+ bool test_deleted(const iterator &it) const {
+ // Invariant: !use_deleted() implies num_deleted is 0.
+ assert(settings.use_deleted() || num_deleted == 0);
+ return num_deleted > 0 && test_deleted_key(get_key(*it));
+ }
+ bool test_deleted(const const_iterator &it) const {
+ // Invariant: !use_deleted() implies num_deleted is 0.
+ assert(settings.use_deleted() || num_deleted == 0);
+ return num_deleted > 0 && test_deleted_key(get_key(*it));
+ }
+
+ private:
+ void check_use_deleted(const char* caller) {
+ (void)caller; // could log it if the assert failed
+ assert(settings.use_deleted());
+ }
+
+ // Write the deleted key to the position specified.
+ // Requires: !test_deleted(it)
+ void set_deleted(iterator &it) {
+ check_use_deleted("set_deleted()");
+ assert(!test_deleted(it));
+ // &* converts from iterator to value-type.
+ set_key(&(*it), key_info.delkey);
+ }
+
+ // We also allow to set/clear the deleted bit on a const iterator.
+ // We allow a const_iterator for the same reason you can delete a
+ // const pointer: it's convenient, and semantically you can't use
+ // 'it' after it's been deleted anyway, so its const-ness doesn't
+ // really matter.
+ // Requires: !test_deleted(it)
+ void set_deleted(const_iterator &it) {
+ check_use_deleted("set_deleted()");
+ assert(!test_deleted(it));
+ set_key(const_cast<pointer>(&(*it)), key_info.delkey);
+ }
+
+ // EMPTY HELPER FUNCTIONS
+ // This lets the user describe a key that will indicate empty (unused)
+ // table entries. This key should be an "impossible" entry --
+ // if you try to insert it for real, you won't be able to retrieve it!
+ // (NB: while you pass in an entire value, only the key part is looked
+ // at. This is just because I don't know how to assign just a key.)
+ public:
+ // These are public so the iterators can use them
+ // True if the item at position bucknum is "empty" marker
+ bool test_empty(size_type bucknum) const {
+ assert(settings.use_empty()); // we always need to know what's empty!
+ return equals(key_info.empty, get_key(table[bucknum]));
+ }
+ bool test_empty(const iterator &it) const {
+ assert(settings.use_empty()); // we always need to know what's empty!
+ return equals(key_info.empty, get_key(*it));
+ }
+ bool test_empty(const const_iterator &it) const {
+ assert(settings.use_empty()); // we always need to know what's empty!
+ return equals(key_info.empty, get_key(*it));
+ }
+
+ private:
+ bool test_empty(size_type bucknum, const_pointer table) const {
+ assert(settings.use_empty());
+ return equals(key_info.empty, get_key(table[bucknum]));
+ }
+
+ void fill_range_with_empty(pointer table_start, pointer table_end) {
+ for (; table_start != table_end; ++table_start) {
+ new (table_start) value_type();
+ set_key(table_start, key_info.empty);
+ }
+ }
+
+ public:
+ // TODO(user): change all callers of this to pass in a key instead,
+ // and take a const key_type instead of const value_type.
+ void set_empty_key(const_reference val) {
+ // Once you set the empty key, you can't change it
+ assert(!settings.use_empty() && "Calling set_empty_key multiple times");
+ // The deleted indicator (if specified) and the empty indicator
+ // must be different.
+ const key_type& key = get_key(val);
+ assert((!settings.use_deleted() || !equals(key, key_info.delkey)) &&
+ "Setting the empty key the same as the deleted key");
+ settings.set_use_empty(true);
+ key_info.empty.~key_type();
+ new (&key_info.empty) key_type(key);
+
+ assert(!table); // must set before first use
+ // num_buckets was set in constructor even though table was nullptr
+ table = get_internal_allocator().allocate(num_buckets);
+ fill_range_with_empty(table, table + num_buckets);
+ }
+ // TODO(user): this should return the key by const reference.
+ value_type empty_key() const {
+ assert(settings.use_empty());
+ value_type ret = value_type();
+ set_key(&ret, key_info.empty);
+ return ret;
+ }
+
+ // FUNCTIONS CONCERNING SIZE
+ public:
+ size_type size() const { return num_elements - num_deleted; }
+ size_type max_size() const { return get_allocator().max_size(); }
+ bool empty() const { return size() == 0; }
+ size_type bucket_count() const { return num_buckets; }
+ size_type max_bucket_count() const { return max_size(); }
+ size_type nonempty_bucket_count() const { return num_elements; }
+ // These are tr1 methods. Their idea of 'bucket' doesn't map well to
+ // what we do. We just say every bucket has 0 or 1 items in it.
+ size_type bucket_size(size_type i) const {
+ return begin(i) == end(i) ? 0 : 1;
+ }
+
+ private:
+ // Because of the above, size_type(-1) is never legal; use it for errors
+ static const size_type ILLEGAL_BUCKET = size_type(-1);
+
+ // Used after a string of deletes. Returns true if we actually shrunk.
+ // TODO(user): take a delta so we can take into account inserts
+ // done after shrinking. Maybe make part of the Settings class?
+ bool maybe_shrink() {
+ assert(num_elements >= num_deleted);
+ assert((bucket_count() & (bucket_count()-1)) == 0); // is a power of two
+ assert(bucket_count() >= HT_MIN_BUCKETS);
+ bool retval = false;
+
+ // If you construct a hashtable with < HT_DEFAULT_STARTING_BUCKETS,
+ // we'll never shrink until you get relatively big, and we'll never
+ // shrink below HT_DEFAULT_STARTING_BUCKETS. Otherwise, something
+ // like "dense_hash_set<int> x; x.insert(4); x.erase(4);" will
+ // shrink us down to HT_MIN_BUCKETS buckets, which is too small.
+ const size_type num_remain = num_elements - num_deleted;
+ const size_type shrink_threshold = settings.shrink_threshold();
+ if (shrink_threshold > 0 && num_remain < shrink_threshold &&
+ bucket_count() > HT_DEFAULT_STARTING_BUCKETS) {
+ const float shrink_factor = settings.shrink_factor();
+ size_type sz = bucket_count() / 2; // find how much we should shrink
+ while (sz > HT_DEFAULT_STARTING_BUCKETS &&
+ num_remain < sz * shrink_factor) {
+ sz /= 2; // stay a power of 2
+ }
+ rebucket(sz);
+ retval = true;
+ }
+ settings.set_consider_shrink(false); // because we just considered it
+ return retval;
+ }
+
+ // We'll let you resize a hashtable -- though this makes us copy all!
+ // When you resize, you say, "make it big enough for this many more elements"
+ // Returns true if we actually resized, false if size was already ok.
+ bool resize_delta(size_type delta) {
+ bool did_resize = false;
+ if (settings.consider_shrink()) { // see if lots of deletes happened
+ if (maybe_shrink())
+ did_resize = true;
+ }
+ if (num_elements >= std::numeric_limits<size_type>::max() - delta) {
+ throw std::length_error("resize overflow");
+ }
+
+ assert(settings.enlarge_threshold() < bucket_count());
+ // Check if our work is done.
+ if (bucket_count() >= HT_MIN_BUCKETS &&
+ num_elements + delta <= settings.enlarge_threshold()) {
+ return did_resize;
+ }
+
+ // Sometimes, we need to resize just to get rid of all the
+ // "deleted" buckets that are clogging up the hashtable. So when
+ // deciding whether to resize, count the deleted buckets (which
+ // are currently taking up room). But later, when we decide what
+ // size to resize to, *don't* count deleted buckets, since they
+ // get discarded during the resize.
+ const size_type needed_size = settings.min_buckets(num_elements + delta, 0);
+ if (needed_size <= bucket_count()) // we have enough buckets
+ return did_resize;
+
+ // We will rebucket.
+ size_type resize_to =
+ settings.min_buckets(num_elements - num_deleted + delta, bucket_count());
+
+ if (resize_to < needed_size) {
+ // This situation means that we have enough deleted elements,
+ // that once we purge them, we won't actually have needed to
+ // grow. But we may want to grow anyway: if we just purge one
+ // element, say, we'll have to grow anyway next time we
+ // insert. Might as well grow now, since we're already going
+ // through the trouble of rebucketing in order to purge the
+ // deleted elements. (Safety note: Can resize_to * 2 overflow? No.
+ // The output of min_buckets() is always a power of two, so resize_to
+ // and needed_size are powers of two. That plus resize_to < needed_size
+ // proves that overflow isn't a concern.)
+ const size_type target =
+ static_cast<size_type>(settings.shrink_size(resize_to*2));
+ if (num_elements - num_deleted + delta >= target) {
+ // Good, we won't be below the shrink threshhold even if we double.
+ resize_to *= 2;
+ }
+ }
+ rebucket(resize_to);
+ return true;
+ }
+
+ // We require table be non-null and empty before calling this.
+ void resize_table(size_type old_size, size_type new_size) {
+ get_internal_allocator().deallocate(table, old_size);
+ table = get_internal_allocator().allocate(new_size);
+ }
+
+ // Copy (or, if Iter is a move_iterator, move) the elements from
+ // [src_first, src_last) into dest_table, which we assume has size
+ // dest_bucket_count and has been initialized to the empty key.
+ template<class Iter>
+ void copy_elements(Iter src_first, Iter src_last, pointer dest_table,
+ size_type dest_bucket_count) {
+ assert((dest_bucket_count & (dest_bucket_count - 1)) == 0); // a power of 2
+ // We use a normal iterator to get non-deleted bcks from ht
+ // We could use insert() here, but since we know there are
+ // no duplicates and no deleted items, we can be more efficient
+ for (; src_first != src_last; ++src_first) {
+ size_type num_probes = 0; // how many times we've probed
+ size_type bucknum;
+ const size_type bucket_count_minus_one = dest_bucket_count - 1;
+ for (bucknum = hash(get_key(*src_first)) & bucket_count_minus_one;
+ !test_empty(bucknum, dest_table); // not empty
+ bucknum = (bucknum +
+ JUMP_(key, num_probes)) & bucket_count_minus_one) {
+ ++num_probes;
+ assert(num_probes < dest_bucket_count
+ && "Hashtable is full: an error in key_equal<> or hash<>");
+ }
+ // Copies or moves the value into dest_table.
+ set_value(&dest_table[bucknum], *src_first);
+ }
+ }
+
+ // Used to actually do the rehashing when we grow/shrink a hashtable
+ void copy_from(const dense_hashtable &ht, size_type min_buckets_wanted) {
+ size_type size = ht.size(); // clear_to_size() sets ht.size() to 0.
+ clear_to_size(settings.min_buckets(ht.size(), min_buckets_wanted));
+ copy_elements(ht.begin(), ht.end(), table, bucket_count());
+ num_elements = size;
+ settings.inc_num_ht_copies();
+ }
+
+ // Rebuckets and resizes the hashtable. Gets rid of any deleted entries.
+ void rebucket(size_type new_num_buckets) {
+ if (table == nullptr) {
+ // When we eventually allocate the table, it will have this many buckets.
+ num_buckets = new_num_buckets;
+ return;
+ }
+ assert(settings.use_empty());
+ assert((new_num_buckets & (new_num_buckets - 1)) == 0); // a power of two
+ // If settings.shrink_factor() is zero then we must not shrink.
+ assert(settings.shrink_factor() > 0 || new_num_buckets >= num_buckets);
+ pointer new_table = get_internal_allocator().allocate(new_num_buckets);
+
+ fill_range_with_empty(new_table, new_table + new_num_buckets);
+ copy_elements(std::make_move_iterator(begin()),
+ std::make_move_iterator(end()),
+ new_table, new_num_buckets);
+
+ destroy_buckets(0, num_buckets); // Destroy table's elements.
+ get_internal_allocator().deallocate(table, num_buckets);
+
+ table = new_table;
+ num_buckets = new_num_buckets;
+ assert(num_elements >= num_deleted);
+ num_elements -= num_deleted;
+ num_deleted = 0;
+ settings.reset_thresholds(bucket_count());
+ settings.inc_num_ht_copies();
+ }
+
+ // Required by the spec for hashed associative container
+ public:
+ // Though the docs say this should be num_buckets, I think it's much
+ // more useful as num_elements. As a special feature, calling with
+ // req_elements==0 will cause us to shrink if we can, saving space.
+ void resize(size_type req_elements) { // resize to this or larger
+ if ( settings.consider_shrink() || req_elements == 0 )
+ maybe_shrink();
+ if ( req_elements > num_elements )
+ resize_delta(req_elements - num_elements);
+ }
+
+ // Get and change the value of shrink_factor and enlarge_factor. The
+ // description at the beginning of this file explains how to choose
+ // the values. Setting the shrink parameter to 0.0 ensures that the
+ // table never shrinks.
+ void get_resizing_parameters(float* shrink, float* grow) const {
+ *shrink = settings.shrink_factor();
+ *grow = settings.enlarge_factor();
+ }
+ void set_resizing_parameters(float shrink, float grow) {
+ settings.set_resizing_parameters(shrink, grow);
+ settings.reset_thresholds(bucket_count());
+ }
+
+ // CONSTRUCTORS -- as required by the specs, we take a size,
+ // but also let you specify a hashfunction, key comparator,
+ // and key extractor. We also define a copy constructor and =.
+ // DESTRUCTOR -- needs to free the table
+ explicit dense_hashtable(size_type expected_max_items_in_table = 0,
+ const HashFcn& hf = HashFcn(),
+ const EqualKey& eql = EqualKey(),
+ const ExtractKey& ext = ExtractKey(),
+ const SetKey& set = SetKey(),
+ const Alloc& alloc = Alloc())
+ : settings(hf),
+ key_info(ext, set, eql, value_alloc_type(alloc)),
+ num_deleted(0),
+ num_elements(0),
+ num_buckets(expected_max_items_in_table == 0
+ ? HT_DEFAULT_STARTING_BUCKETS
+ : settings.min_buckets(expected_max_items_in_table, 0)),
+ table(nullptr) {
+ // table is nullptr until the empty key is set. However, we set num_buckets
+ // here so we know how much space to allocate once the empty key is set.
+ settings.reset_thresholds(bucket_count());
+ }
+
+ // As a convenience for resize(), we allow an optional second argument
+ // which lets you make this new hashtable a different size than ht
+ dense_hashtable(const dense_hashtable& ht,
+ size_type min_buckets_wanted = HT_DEFAULT_STARTING_BUCKETS)
+ : settings(ht.settings),
+ key_info(ht.key_info.as_extract_key(), ht.key_info.as_set_key(),
+ ht.key_info.as_equal_key(),
+ value_alloc_type(
+ std::allocator_traits<value_alloc_type>::
+ select_on_container_copy_construction(
+ ht.key_info.as_value_alloc()))),
+ num_deleted(0),
+ num_elements(0),
+ num_buckets(0),
+ table(nullptr) {
+ key_info.delkey = ht.key_info.delkey;
+ key_info.empty = ht.key_info.empty;
+ if (!ht.settings.use_empty()) {
+ // If use_empty isn't set, copy_from will crash, so we do our own copying.
+ assert(ht.empty());
+ num_buckets = settings.min_buckets(ht.size(), min_buckets_wanted);
+ settings.reset_thresholds(bucket_count());
+ return;
+ }
+ settings.reset_thresholds(bucket_count());
+ copy_from(ht, min_buckets_wanted); // copy_from() ignores deleted entries
+ }
+
+ dense_hashtable& operator=(const dense_hashtable& ht) {
+ if (&ht == this) return *this; // don't copy onto ourselves
+ settings = ht.settings;
+ key_info.as_extract_key() = ht.key_info.as_extract_key();
+ key_info.as_set_key() = ht.key_info.as_set_key();
+ key_info.as_equal_key() = ht.key_info.as_equal_key();
+ if (std::allocator_traits<
+ value_alloc_type>::propagate_on_container_copy_assignment::value) {
+ // If we're about to overwrite our allocator, we need to free all
+ // memory using our old allocator.
+ if (key_info.as_value_alloc() != ht.key_info.as_value_alloc()) {
+ destroy_table();
+ }
+ static_cast<value_alloc_type&>(key_info) =
+ static_cast<const value_alloc_type&>(ht.key_info);
+ }
+ key_info.empty = ht.key_info.empty;
+ key_info.delkey = ht.key_info.delkey;
+
+ if (ht.settings.use_empty()) {
+ // copy_from() calls clear and sets num_deleted to 0 too
+ copy_from(ht, HT_MIN_BUCKETS);
+ } else {
+ assert(ht.empty());
+ destroy_table();
+ }
+
+ // we purposefully don't copy the allocator, which may not be copyable
+ return *this;
+ }
+
+ dense_hashtable(dense_hashtable&& ht)
+ : settings(std::move(ht.settings)),
+ key_info(std::move(ht.key_info)),
+ num_deleted(ht.num_deleted),
+ num_elements(ht.num_elements),
+ num_buckets(ht.num_buckets),
+ table(ht.table) {
+ ht.num_deleted = 0;
+ ht.num_elements = 0;
+ ht.table = nullptr;
+ ht.num_buckets = HT_DEFAULT_STARTING_BUCKETS;
+ ht.settings.set_use_empty(false);
+ ht.settings.set_use_deleted(false);
+ }
+
+ dense_hashtable& operator=(dense_hashtable&& ht) {
+ if (&ht == this) return *this; // don't move onto ourselves
+
+ const bool can_move_table =
+ std::allocator_traits<
+ Alloc>::propagate_on_container_move_assignment::value ||
+ key_info.as_value_alloc() == ht.key_info.as_value_alloc();
+
+ // First, deallocate with this's allocator.
+ destroy_table();
+
+ if (std::allocator_traits<
+ value_alloc_type>::propagate_on_container_move_assignment::value) {
+ // This moves the allocator.
+ key_info = std::move(ht.key_info);
+ } else {
+ // Move all other base classes of key_info from ht, but don't move the
+ // allocator.
+ key_info.as_extract_key() = std::move(ht.key_info.as_extract_key());
+ key_info.as_set_key() = std::move(ht.key_info.as_set_key());
+ key_info.as_equal_key() = std::move(ht.key_info.as_equal_key());
+ key_info.delkey = std::move(ht.key_info.delkey);
+ key_info.empty = std::move(ht.key_info.empty);
+ }
+
+ settings = std::move(ht.settings);
+ num_deleted = ht.num_deleted;
+ ht.num_deleted = 0;
+ num_elements = ht.num_elements;
+ ht.num_elements = 0;
+ num_buckets = ht.num_buckets;
+ ht.num_buckets = HT_DEFAULT_STARTING_BUCKETS;
+ ht.settings.set_use_empty(false);
+ ht.settings.set_use_deleted(false);
+
+ if (can_move_table) {
+ // We can transfer ownership of the table from ht to this because either
+ // we're propagating the allocator or ht's allocator is equal to this's.
+ table = ht.table;
+ ht.table = nullptr;
+ } else if (ht.table) {
+ // We can't transfer ownership of any memory from ht to this, so the
+ // best we can do is move element-by-element.
+ table = get_internal_allocator().allocate(num_buckets);
+ for (size_type i = 0; i < num_buckets; ++i) {
+ new(table + i) Value(std::move(ht.table[i]));
+ }
+ ht.destroy_table();
+ }
+
+ return *this;
+ }
+
+ ~dense_hashtable() { destroy_table(); }
+
+ // Many STL algorithms use swap instead of copy constructors
+ void swap(dense_hashtable& ht) {
+ if (this == &ht) return; // swap with self.
+ using std::swap;
+ swap(settings, ht.settings);
+ // Swap everything in key_info but the allocator.
+ swap(key_info.as_extract_key(), ht.key_info.as_extract_key());
+ swap(key_info.as_set_key(), ht.key_info.as_set_key());
+ swap(key_info.as_equal_key(), ht.key_info.as_equal_key());
+ if (std::allocator_traits<
+ value_alloc_type>::propagate_on_container_swap::value) {
+ swap(static_cast<value_alloc_type&>(key_info),
+ static_cast<value_alloc_type&>(ht.key_info));
+ } else {
+ // Swapping when allocators are unequal and
+ // propagate_on_container_swap is false is undefined behavior.
+ S2_CHECK(key_info.as_value_alloc() == ht.key_info.as_value_alloc());
+ }
+ swap(key_info.empty, ht.key_info.empty);
+ swap(key_info.delkey, ht.key_info.delkey);
+ swap(num_deleted, ht.num_deleted);
+ swap(num_elements, ht.num_elements);
+ swap(num_buckets, ht.num_buckets);
+ swap(table, ht.table);
+ }
+
+ private:
+ void destroy_table() {
+ if (table) {
+ destroy_buckets(0, num_buckets);
+ get_internal_allocator().deallocate(table, num_buckets);
+ table = nullptr;
+ }
+ }
+
+ void clear_to_size(size_type new_num_buckets) {
+ if (!table) {
+ table = get_internal_allocator().allocate(new_num_buckets);
+ } else {
+ destroy_buckets(0, num_buckets);
+ if (new_num_buckets != num_buckets) { // resize, if necessary
+ resize_table(num_buckets, new_num_buckets);
+ }
+ }
+ assert(table);
+ fill_range_with_empty(table, table + new_num_buckets);
+ num_elements = 0;
+ num_deleted = 0;
+ num_buckets = new_num_buckets; // our new size
+ settings.reset_thresholds(bucket_count());
+ }
+
+ public:
+ // It's always nice to be able to clear a table without deallocating it
+ void clear() {
+ // If the table is already empty, and the number of buckets is
+ // already as we desire, there's nothing to do.
+ const size_type new_num_buckets = settings.min_buckets(0, 0);
+ if (num_elements == 0 && new_num_buckets == num_buckets) {
+ return;
+ }
+ clear_to_size(new_num_buckets);
+ }
+
+ // Clear the table without resizing it.
+ // Mimicks the stl_hashtable's behaviour when clear()-ing in that it
+ // does not modify the bucket count
+ void clear_no_resize() {
+ if (num_elements > 0) {
+ assert(table);
+ destroy_buckets(0, num_buckets);
+ fill_range_with_empty(table, table + num_buckets);
+ }
+ // don't consider to shrink before another erase()
+ settings.reset_thresholds(bucket_count());
+ num_elements = 0;
+ num_deleted = 0;
+ }
+
+ // LOOKUP ROUTINES
+ private:
+ template <class K>
+ void assert_key_is_not_empty_or_deleted(const K& key) const {
+ assert(settings.use_empty() && "set_empty_key() was not called");
+ assert(!equals(key, key_info.empty) &&
+ "Using the empty key as a regular key");
+ assert((!settings.use_deleted() || !equals(key, key_info.delkey))
+ && "Using the deleted key as a regular key");
+ }
+
+ template <class K>
+ std::pair<size_type, size_type> find_position(const K& key) const {
+ return find_position_using_hash(hash(key), key);
+ }
+
+ // Returns a pair of positions: 1st where the object is, 2nd where
+ // it would go if you wanted to insert it. 1st is ILLEGAL_BUCKET
+ // if object is not found; 2nd is ILLEGAL_BUCKET if it is.
+ // Note: because of deletions where-to-insert is not trivial: it's the
+ // first deleted bucket we see, as long as we don't find the key later
+ template <class K>
+ std::pair<size_type, size_type> find_position_using_hash(
+ const size_type key_hash, const K& key) const {
+ assert_key_is_not_empty_or_deleted(key);
+ size_type num_probes = 0; // how many times we've probed
+ const size_type bucket_count_minus_one = bucket_count() - 1;
+ size_type bucknum = key_hash & bucket_count_minus_one;
+ size_type insert_pos = ILLEGAL_BUCKET; // where we would insert
+ while (1) { // probe until something happens
+ if (test_empty(bucknum)) { // bucket is empty
+ if (insert_pos == ILLEGAL_BUCKET) // found no prior place to insert
+ return std::pair<size_type, size_type>(ILLEGAL_BUCKET, bucknum);
+ else
+ return std::pair<size_type, size_type>(ILLEGAL_BUCKET, insert_pos);
+
+ } else if (test_deleted(bucknum)) {
+ // keep searching, but mark to insert
+ if ( insert_pos == ILLEGAL_BUCKET )
+ insert_pos = bucknum;
+
+ } else if (equals(key, get_key(table[bucknum]))) {
+ return std::pair<size_type, size_type>(bucknum, ILLEGAL_BUCKET);
+ }
+ ++num_probes; // we're doing another probe
+ bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one;
+ assert(num_probes < bucket_count()
+ && "Hashtable is full: an error in key_equal<> or hash<>");
+ }
+ }
+
+ template <class K>
+ std::pair<size_type, bool> find_if_present(const K& key) const {
+ return find_if_present_using_hash(hash(key), key);
+ }
+
+ // Return where the key is (if at all), and if it is present. If
+ // the key isn't present then the first part of the return value is
+ // undefined. The same information can be extracted from the result
+ // of find_position(), but that tends to be slower in practice.
+ template <class K>
+ std::pair<size_type, bool> find_if_present_using_hash(
+ const size_type key_hash, const K& key) const {
+ assert_key_is_not_empty_or_deleted(key);
+ size_type num_probes = 0; // how many times we've probed
+ const size_type bucket_count_minus_one = bucket_count() - 1;
+ size_type bucknum = key_hash & bucket_count_minus_one;
+ while (1) { // probe until something happens
+ if (equals(key, get_key(table[bucknum]))) {
+ return std::pair<size_type, bool>(bucknum, true);
+ } else if (test_empty(bucknum)) {
+ return std::pair<size_type, bool>(0, false);
+ }
+ ++num_probes; // we're doing another probe
+ bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one;
+ assert(num_probes < bucket_count()
+ && "Hashtable is full: an error in key_equal<> or hash<>");
+ }
+ }
+
+ private:
+ template <class K>
+ iterator find_impl(const K& key) {
+ std::pair<size_type, bool> pos = find_if_present(key);
+ return pos.second ?
+ iterator(this, table + pos.first, table + num_buckets, false) :
+ end();
+ }
+
+ template <class K>
+ const_iterator find_impl(const K& key) const {
+ std::pair<size_type, bool> pos = find_if_present(key);
+ return pos.second ?
+ const_iterator(this, table + pos.first, table + num_buckets, false) :
+ end();
+ }
+
+ template <class K>
+ size_type bucket_impl(const K& key) const {
+ std::pair<size_type, size_type> pos = find_position(key);
+ return pos.first == ILLEGAL_BUCKET ? pos.second : pos.first;
+ }
+
+ template <class K>
+ size_type count_impl(const K& key) const {
+ return find_if_present(key).second ? 1 : 0;
+ }
+
+ template <class K>
+ std::pair<iterator, iterator>
+ equal_range_impl(const K& key) {
+ iterator pos = find(key);
+ if (pos == end()) {
+ return std::pair<iterator, iterator>(pos, pos);
+ } else {
+ const iterator startpos = pos++;
+ return std::pair<iterator, iterator>(startpos, pos);
+ }
+ }
+
+ template <class K>
+ std::pair<const_iterator, const_iterator>
+ equal_range_impl(const K& key) const {
+ const_iterator pos = find(key);
+ if (pos == end()) {
+ return std::pair<const_iterator, const_iterator>(pos, pos);
+ } else {
+ const const_iterator startpos = pos++;
+ return std::pair<const_iterator, const_iterator>(startpos, pos);
+ }
+ }
+
+ public:
+ iterator find(const key_type& key) { return find_impl(key); }
+
+ const_iterator find(const key_type& key) const { return find_impl(key); }
+
+ // This is a tr1 method: the bucket a given key is in, or what bucket
+ // it would be put in, if it were to be inserted. Shrug.
+ size_type bucket(const key_type& key) const { return bucket_impl(key); }
+
+ // Counts how many elements have key key. For maps, it's either 0 or 1.
+ size_type count(const key_type &key) const { return count_impl(key); }
+
+ // Likewise, equal_range doesn't really make sense for us. Oh well.
+ std::pair<iterator, iterator>
+ equal_range(const key_type& key) { return equal_range_impl(key); }
+
+ std::pair<const_iterator, const_iterator>
+ equal_range(const key_type& key) const { return equal_range_impl(key); }
+
+
+
+ // INSERTION ROUTINES
+ private:
+ // Private method used by insert_noresize and find_or_insert.
+ // 'obj' is either value_type&& or const value_type&.
+ template <typename U>
+ iterator insert_at(U&& obj, size_type pos) {
+ if (size() >= max_size()) {
+ throw std::length_error("insert overflow");
+ }
+ if ( test_deleted(pos) ) { // just replace if it's been del.
+ assert(num_deleted > 0);
+ --num_deleted; // used to be, now it isn't
+ } else {
+ ++num_elements; // replacing an empty bucket
+ }
+ set_value(&table[pos], std::forward<U>(obj));
+ return iterator(this, table + pos, table + num_buckets, false);
+ }
+
+ // If you know *this is big enough to hold obj, use this routine
+ // 'obj' is value_type&& or const value_type&.
+ template <typename U>
+ std::pair<iterator, bool> insert_noresize(U&& obj) { // NOLINT
+ return insert_noresize_using_hash(hash(get_key(obj)), std::forward<U>(obj));
+ }
+
+ // If you know *this is big enough to hold obj, use this routine
+ // 'obj' is value_type&& or const value_type&.
+ template <typename U>
+ std::pair<iterator, bool> insert_noresize_using_hash(const size_type key_hash,
+ U&& obj) {
+ const std::pair<size_type, size_type> pos =
+ find_position_using_hash(key_hash, get_key(obj));
+ if (pos.first != ILLEGAL_BUCKET) { // object was already there
+ return std::pair<iterator, bool>(iterator(this, table + pos.first,
+ table + num_buckets, false),
+ false); // false: we didn't insert
+ } else { // pos.second says where to put it
+ iterator i = insert_at(std::forward<U>(obj), pos.second);
+ return std::pair<iterator, bool>(i, true);
+ }
+ }
+
+ // Specializations of insert(it, it) depending on the power of the iterator:
+ // (1) Iterator supports operator-, resize before inserting
+ template <class ForwardIterator>
+ void insert(ForwardIterator f, ForwardIterator l, std::forward_iterator_tag) {
+ size_t dist = std::distance(f, l);
+ if (dist >= std::numeric_limits<size_type>::max()) {
+ throw std::length_error("insert-range overflow");
+ }
+ resize_delta(static_cast<size_type>(dist));
+ for ( ; dist > 0; --dist, ++f) {
+ insert_noresize(*f);
+ }
+ }
+
+ // (2) Arbitrary iterator, can't tell how much to resize
+ template <class InputIterator>
+ void insert(InputIterator f, InputIterator l, std::input_iterator_tag) {
+ for ( ; f != l; ++f)
+ insert(*f);
+ }
+
+ public:
+ // This is the normal insert routine, used by the outside world
+ std::pair<iterator, bool> insert(const value_type& obj) {
+ resize_delta(1); // adding an object, grow if need be
+ return insert_noresize(obj);
+ }
+
+ std::pair<iterator, bool> insert(value_type&& obj) { // NOLINT
+ resize_delta(1); // adding an object, grow if need be
+ return insert_noresize(std::move(obj));
+ }
+
+ // When inserting a lot at a time, we specialize on the type of iterator
+ template <class InputIterator>
+ void insert(InputIterator f, InputIterator l) {
+ // specializes on iterator type
+ insert(f, l,
+ typename std::iterator_traits<InputIterator>::iterator_category());
+ }
+
+ template <class DefaultValue>
+ value_type& find_or_insert(const key_type& key) {
+ return find_or_insert_using_hash<DefaultValue>(hash(key), key);
+ }
+
+ // DefaultValue is a functor that takes a key and returns a value_type
+ // representing the default value to be inserted if none is found.
+ template <class DefaultValue>
+ value_type& find_or_insert_using_hash(const size_type key_hash,
+ const key_type& key) {
+ const std::pair<size_type, size_type> pos =
+ find_position_using_hash(key_hash, key);
+ DefaultValue default_value;
+ if (pos.first != ILLEGAL_BUCKET) { // object was already there
+ return table[pos.first];
+ } else if (resize_delta(1)) { // needed to rehash to make room
+ // Since we resized, we can't use pos, so recalculate where to insert.
+ return *insert_noresize(default_value(key)).first;
+ } else { // no need to rehash, insert right here
+ return *insert_at(default_value(key), pos.second);
+ }
+ }
+
+
+ // DELETION ROUTINES
+ private:
+ template <class K>
+ size_type erase_impl(const K& key) {
+ iterator pos = find(key);
+ if (pos != end()) {
+ assert(!test_deleted(pos)); // or find() shouldn't have returned it
+ set_deleted(pos);
+ ++num_deleted;
+ // will think about shrink after next insert
+ settings.set_consider_shrink(true);
+ return 1; // because we deleted one thing
+ } else {
+ return 0; // because we deleted nothing
+ }
+ }
+
+ public:
+ size_type erase(const key_type& key) {
+ return erase_impl(key);
+ }
+
+
+ void erase(iterator pos) {
+ if (pos == end()) return; // sanity check
+ set_deleted(pos);
+ ++num_deleted;
+ // will think about shrink after next insert
+ settings.set_consider_shrink(true);
+ }
+
+ void erase(iterator f, iterator l) {
+ for (; f != l; ++f) {
+ set_deleted(f);
+ ++num_deleted;
+ }
+ // will think about shrink after next insert
+ settings.set_consider_shrink(true);
+ }
+
+ // We allow you to erase a const_iterator just like we allow you to
+ // erase an iterator. This is in parallel to 'delete': you can delete
+ // a const pointer just like a non-const pointer. The logic is that
+ // you can't use the object after it's erased anyway, so it doesn't matter
+ // if it's const or not.
+ void erase(const_iterator pos) {
+ if (pos == end()) return; // sanity check
+ set_deleted(pos);
+ ++num_deleted;
+ // will think about shrink after next insert
+ settings.set_consider_shrink(true);
+ }
+ void erase(const_iterator f, const_iterator l) {
+ for ( ; f != l; ++f) {
+ set_deleted(f);
+ ++num_deleted;
+ }
+ // will think about shrink after next insert
+ settings.set_consider_shrink(true);
+ }
+
+
+ // COMPARISON
+ bool operator==(const dense_hashtable& ht) const {
+ if (size() != ht.size()) {
+ return false;
+ } else if (this == &ht) {
+ return true;
+ } else {
+ // Iterate through the elements in "this" and see if the
+ // corresponding element is in ht
+ for ( const_iterator it = begin(); it != end(); ++it ) {
+ const_iterator it2 = ht.find(get_key(*it));
+ if ((it2 == ht.end()) || (*it != *it2)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ bool operator!=(const dense_hashtable& ht) const {
+ return !(*this == ht);
+ }
+
+
+ // I/O
+ // We support reading and writing hashtables to disk. Alas, since
+ // I don't know how to write a hasher or key_equal, you have to make
+ // sure everything but the table is the same. We compact before writing.
+ private:
+ // Every time the disk format changes, this should probably change too
+ typedef unsigned long MagicNumberType;
+ static const MagicNumberType MAGIC_NUMBER = 0x13578642;
+
+ // Package functors with another class to eliminate memory needed for
+ // zero-size functors. Since ExtractKey and hasher's operator() might
+ // have the same function signature, they must be packaged in
+ // different classes.
+ struct Settings :
+ sh_hashtable_settings<key_type, hasher, size_type, HT_MIN_BUCKETS> {
+ explicit Settings(const hasher& hf)
+ : sh_hashtable_settings<key_type, hasher, size_type, HT_MIN_BUCKETS>(
+ hf, HT_OCCUPANCY_PCT / 100.0f, HT_EMPTY_PCT / 100.0f) {}
+ };
+
+ // Packages ExtractKey, SetKey, EqualKey functors, allocator and deleted and
+ // empty key values.
+ struct KeyInfo : public ExtractKey,
+ public SetKey,
+ public EqualKey,
+ public value_alloc_type {
+ KeyInfo(const ExtractKey& ek, const SetKey& sk, const EqualKey& eq,
+ const value_alloc_type& a)
+ : ExtractKey(ek),
+ SetKey(sk),
+ EqualKey(eq),
+ value_alloc_type(a),
+ delkey(),
+ empty() {}
+
+ // Accessors for convenient access to base classes.
+ ExtractKey& as_extract_key() { return *this; }
+ const ExtractKey& as_extract_key() const { return *this; }
+ SetKey& as_set_key() { return *this; }
+ const SetKey& as_set_key() const { return *this; }
+ EqualKey& as_equal_key() { return *this; }
+ const EqualKey& as_equal_key() const { return *this; }
+ value_alloc_type& as_value_alloc() { return *this; }
+ const value_alloc_type& as_value_alloc() const { return *this; }
+
+ // We want to return the exact same type as ExtractKey: Key or const Key&
+ typename ExtractKey::result_type get_key(const_reference v) const {
+ return ExtractKey::operator()(v);
+ }
+ void set_key(pointer v, const key_type& k) const {
+ SetKey::operator()(v, k);
+ }
+
+ // We only ever call EqualKey::operator()(key_type, K) -- we never use the
+ // other order of args. This allows consumers to get away with implementing
+ // only half of operator==.
+ template <class K>
+ bool equals(const key_type& a, const K& b) const {
+ return EqualKey::operator()(a, b);
+ }
+
+ pointer allocate(size_type size) {
+ pointer memory = value_alloc_type::allocate(size);
+ assert(memory != nullptr);
+ return memory;
+ }
+
+ // Which key marks deleted entries.
+ // TODO(user): make a pointer, and get rid of use_deleted (benchmark!)
+ typename std::remove_const<key_type>::type delkey;
+ // Key value used to mark unused entries.
+ typename std::remove_const<key_type>::type empty;
+ };
+
+ // Returns the value_alloc_type used to allocate and deallocate
+ // the table. This can be different from the one returned by get_allocator().
+ value_alloc_type& get_internal_allocator() { return key_info; }
+
+ // Utility functions to access the templated operators
+ size_type hash(const key_type& v) const {
+ return settings.hash(v);
+ }
+ bool equals(const key_type& a, const key_type& b) const {
+ return key_info.equals(a, b);
+ }
+
+
+ typename ExtractKey::result_type get_key(const_reference v) const {
+ return key_info.get_key(v);
+ }
+ void set_key(pointer v, const key_type& k) const {
+ key_info.set_key(v, k);
+ }
+
+ private:
+ // Actual data
+ Settings settings;
+ KeyInfo key_info;
+
+ size_type num_deleted; // how many occupied buckets are marked deleted
+ size_type num_elements;
+ size_type num_buckets;
+ pointer table;
+};
+
+
+// We need a global swap as well
+template <class V, class K, class HF, class ExK, class SetK, class EqK, class A>
+inline void swap(dense_hashtable<V, K, HF, ExK, SetK, EqK, A> &x,
+ dense_hashtable<V, K, HF, ExK, SetK, EqK, A> &y) {
+ x.swap(y);
+}
+
+#undef JUMP_
+#undef PUT_
+#undef GET_
+
+template <class V, class K, class HF, class ExK, class SetK, class EqK, class A>
+const typename dense_hashtable<V, K, HF, ExK, SetK, EqK, A>::size_type
+ dense_hashtable<V, K, HF, ExK, SetK, EqK, A>::ILLEGAL_BUCKET;
+
+// How full we let the table get before we resize. Knuth says .8 is
+// good -- higher causes us to probe too much, though saves memory.
+// However, we go with .5, getting better performance at the cost of
+// more space (a trade-off densehashtable explicitly chooses to make).
+// Feel free to play around with different values, though, via
+// max_load_factor() and/or set_resizing_parameters().
+template <class V, class K, class HF, class ExK, class SetK, class EqK, class A>
+const int dense_hashtable<V,K,HF,ExK,SetK,EqK,A>::HT_OCCUPANCY_PCT = 50;
+
+// How empty we let the table get before we resize lower.
+// It should be less than OCCUPANCY_PCT / 2 or we thrash resizing.
+template <class V, class K, class HF, class ExK, class SetK, class EqK, class A>
+const int dense_hashtable<V, K, HF, ExK, SetK, EqK, A>::HT_EMPTY_PCT =
+ static_cast<int>(
+ 0.4 * dense_hashtable<V, K, HF, ExK, SetK, EqK, A>::HT_OCCUPANCY_PCT);
+
+}
+
+#endif // S2_UTIL_GTL_DENSEHASHTABLE_H_
--- /dev/null
+// Copyright 2010 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// ---
+
+#ifndef S2_UTIL_GTL_HASHTABLE_COMMON_H_
+#define S2_UTIL_GTL_HASHTABLE_COMMON_H_
+
+#include <cassert>
+#include <cstddef>
+#include <algorithm>
+
+#include <stdexcept> // For length_error
+
+// Settings contains parameters for growing and shrinking the table.
+// It also packages zero-size functor (ie. hasher). One invariant
+// enforced in enlarge_size() is that we never allow all slots
+// occupied. (This is unlikely to matter to users, because using
+// a load near 1 is slow and not recommended. It allows other code
+// to assume there is at least one empty bucket.)
+//
+// It does some munging of the hash value in cases where we think
+// (fear) the original hash function might not be very good. In
+// particular, the default hash of pointers is the identity hash,
+// so probably all the low bits are 0. We identify when we think
+// we're hashing a pointer, and chop off the low bits. Note this
+// isn't perfect: even when the key is a pointer, we can't tell
+// for sure that the hash is the identity hash. If it's not, this
+// is needless work (and possibly, though not likely, harmful).
+
+template<typename Key, typename HashFunc,
+ typename SizeType, int HT_MIN_BUCKETS>
+class sh_hashtable_settings : public HashFunc {
+ public:
+ typedef Key key_type;
+ typedef HashFunc hasher;
+ typedef SizeType size_type;
+
+ public:
+ sh_hashtable_settings(const hasher& hf,
+ const float ht_occupancy_flt,
+ const float ht_empty_flt)
+ : hasher(hf),
+ enlarge_threshold_(0),
+ shrink_threshold_(0),
+ consider_shrink_(false),
+ use_empty_(false),
+ use_deleted_(false),
+ num_ht_copies_(0) {
+ set_enlarge_factor(ht_occupancy_flt);
+ set_shrink_factor(ht_empty_flt);
+ }
+
+ template<class K>
+ size_type hash(const K& v) const {
+ // We munge the hash value when we don't trust hasher::operator(). It is
+ // very important that we use hash_munger<Key> instead of hash_munger<K>.
+ // Within a given hashtable, all hash values must be munged in the same way.
+ return hash_munger<Key>::MungedHash(hasher::operator()(v));
+ }
+
+ float enlarge_factor() const {
+ return enlarge_factor_;
+ }
+ void set_enlarge_factor(float f) {
+ enlarge_factor_ = f;
+ }
+ float shrink_factor() const {
+ return shrink_factor_;
+ }
+ void set_shrink_factor(float f) {
+ shrink_factor_ = f;
+ }
+
+ size_type enlarge_threshold() const {
+ return enlarge_threshold_;
+ }
+ void set_enlarge_threshold(size_type t) {
+ enlarge_threshold_ = t;
+ }
+ size_type shrink_threshold() const {
+ return shrink_threshold_;
+ }
+ void set_shrink_threshold(size_type t) {
+ shrink_threshold_ = t;
+ }
+
+ size_type enlarge_size(size_type x) const {
+ return std::min<size_type>(x - 1, x * enlarge_factor_);
+ }
+ size_type shrink_size(size_type x) const {
+ return static_cast<size_type>(x * shrink_factor_);
+ }
+
+ bool consider_shrink() const {
+ return consider_shrink_;
+ }
+ void set_consider_shrink(bool t) {
+ consider_shrink_ = t;
+ }
+
+ bool use_empty() const {
+ return use_empty_;
+ }
+ void set_use_empty(bool t) {
+ use_empty_ = t;
+ }
+
+ bool use_deleted() const {
+ return use_deleted_;
+ }
+ void set_use_deleted(bool t) {
+ use_deleted_ = t;
+ }
+
+ size_type num_ht_copies() const {
+ return static_cast<size_type>(num_ht_copies_);
+ }
+ void inc_num_ht_copies() {
+ ++num_ht_copies_;
+ }
+
+ // Reset the enlarge and shrink thresholds
+ void reset_thresholds(size_type num_buckets) {
+ set_enlarge_threshold(enlarge_size(num_buckets));
+ set_shrink_threshold(shrink_size(num_buckets));
+ // whatever caused us to reset already considered
+ set_consider_shrink(false);
+ }
+
+ // Caller is resposible for calling reset_threshold right after
+ // set_resizing_parameters.
+ void set_resizing_parameters(float shrink, float grow) {
+ assert(shrink >= 0.0);
+ assert(grow <= 1.0);
+ if (shrink > grow/2.0f)
+ shrink = grow / 2.0f; // otherwise we thrash hashtable size
+ set_shrink_factor(shrink);
+ set_enlarge_factor(grow);
+ }
+
+ // This is the smallest size a hashtable can be without being too crowded.
+ // If you like, you can give a min #buckets as well as a min #elts.
+ // This is guaranteed to return a power of two.
+ size_type min_buckets(size_type num_elts, size_type min_buckets_wanted) {
+ float enlarge = enlarge_factor();
+ size_type sz = HT_MIN_BUCKETS; // min buckets allowed
+ while ( sz < min_buckets_wanted ||
+ num_elts >= static_cast<size_type>(sz * enlarge) ) {
+ // This just prevents overflowing size_type, since sz can exceed
+ // max_size() here.
+ if (static_cast<size_type>(sz * 2) < sz) {
+ throw std::length_error("resize overflow"); // protect against overflow
+ }
+ sz *= 2;
+ }
+ return sz;
+ }
+
+ private:
+ template<class HashKey> class hash_munger {
+ public:
+ static size_t MungedHash(size_t hash) {
+ return hash;
+ }
+ };
+ // This matches when the hashtable key is a pointer.
+ template<class HashKey> class hash_munger<HashKey*> {
+ public:
+ static size_t MungedHash(size_t hash) {
+ // TODO(user): consider rotating instead:
+ // static const int shift = (sizeof(void *) == 4) ? 2 : 3;
+ // return (hash << (sizeof(hash) * 8) - shift)) | (hash >> shift);
+ // This matters if we ever change sparse/dense_hash_* to compare
+ // hashes before comparing actual values. It's speedy on x86.
+ return hash / sizeof(void*); // get rid of known-0 bits
+ }
+ };
+
+ size_type enlarge_threshold_; // table.size() * enlarge_factor
+ size_type shrink_threshold_; // table.size() * shrink_factor
+ float enlarge_factor_; // how full before resize
+ float shrink_factor_; // how empty before resize
+ // consider_shrink=true if we should try to shrink before next insert
+ bool consider_shrink_;
+ bool use_empty_; // used only by densehashtable, not sparsehashtable
+ bool use_deleted_; // false until delkey has been set
+ // num_ht_copies is a counter incremented every Copy/Move
+ unsigned int num_ht_copies_;
+};
+
+// This traits class checks whether T::is_transparent exists and names a type.
+//
+// struct Foo { using is_transparent = void; };
+// struct Bar {};
+// static_assert(sh_is_transparent<Foo>::value, "Foo is transparent.");
+// staitc_assert(!sh_is_transparent<Bar>::value, "Bar is not transparent.");
+template<class T>
+struct sh_is_transparent {
+ private:
+ struct No { char x; };
+ struct Yes { No x[2]; };
+
+ template <class U>
+ static Yes Test(typename U::is_transparent*);
+ template<class U> static No Test(...);
+
+ public:
+ enum { value = sizeof(Test<T>(nullptr)) == sizeof(Yes) };
+};
+
+
+#endif // S2_UTIL_GTL_HASHTABLE_COMMON_H_
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef S2_UTIL_GTL_LAYOUT_H_
+#define S2_UTIL_GTL_LAYOUT_H_
+
+#include "s2/third_party/absl/container/internal/layout.h"
+
+namespace gtl {
+
+using absl::container_internal::Aligned;
+using absl::container_internal::Layout;
+
+} // namespace gtl
+
+#endif // S2_UTIL_GTL_LAYOUT_H_
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// gtl::legacy_random_shuffle is similar in API and behavior to
+// std::random_shuffle, which was removed in C++17.
+//
+// When built for Linux production targets using crosstool 18,
+// these APIs produce the same results as std::random_shuffle.
+//
+// Otherwise, the specification for these functions reverts to that
+// of std::random_shuffle as specified in C++11. In particular,
+// these functions do not promise to produce the same shuffle
+// sequences forever.
+//
+// These are deprecated, and intended to be used only for legacy
+// code that must move off std::random_shuffle simply because the
+// function is not part of C++17.
+#ifndef S2_UTIL_GTL_LEGACY_RANDOM_SHUFFLE_H_
+#define S2_UTIL_GTL_LEGACY_RANDOM_SHUFFLE_H_
+
+#include <algorithm>
+#include <cstdlib>
+#include <iterator>
+
+#include "s2/third_party/absl/base/macros.h"
+
+namespace gtl {
+
+// Reorders the elements in the range `[begin, last)` randomly. The
+// random number generator `rnd` must be a function object returning a
+// randomly chosen value of type convertible to and from
+// `std::iterator_traits<RandomIt>::difference_type` in the interval
+// `[0,n)` if invoked as `r(n)`.
+//
+// This function is deprecated. See the file comment above for
+// additional details.
+template <class RandomIt, class RandomFunc>
+ABSL_DEPRECATED("Use std::shuffle instead; see go/nors-legacy-api")
+void legacy_random_shuffle(const RandomIt begin, const RandomIt end,
+ RandomFunc&& rnd) {
+ auto size = std::distance(begin, end);
+ for (decltype(size) i = 1; i < size; ++i) {
+ // Loop invariant: elements below i are uniformly shuffled.
+ std::iter_swap(begin + i, begin + rnd(i + 1));
+ }
+}
+
+// Reorders the elements in the range `[begin, last)` randomly. The
+// random number generator is `std::rand()`.
+//
+// This function is deprecated. See the file comment above for
+// additional details.
+template <class RandomIt>
+ABSL_DEPRECATED("Use std::shuffle instead; see go/nors-legacy-api")
+void legacy_random_shuffle(RandomIt begin, RandomIt end) {
+ legacy_random_shuffle(
+ begin, end,
+ [](typename std::iterator_traits<RandomIt>::difference_type i) {
+ return std::rand() % i;
+ });
+}
+
+} // namespace gtl
+
+#endif // S2_UTIL_GTL_LEGACY_RANDOM_SHUFFLE_H_
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: jyrki@google.com (Jyrki Alakuijala)
+//
+// This file contains routines for mixing hashes.
+
+#ifndef S2_UTIL_HASH_MIX_H_
+#define S2_UTIL_HASH_MIX_H_
+
+#include <cstddef>
+#include <limits>
+
+// Fast mixing of hash values -- not strong enough for fingerprinting.
+// May change from time to time.
+//
+// Values given are expected to be hashes from good hash functions.
+// What constitutes a good hash may depend on your application. As a rule of
+// thumb, if std::hash<int> is strong enough for your hashing need if
+// your data were just ints, it will most likely be the correct choice
+// for a mixed hash of data members. HashMix does one round of multiply and
+// rotate mixing, so you get some additional collision avoidance guarantees
+// compared to just using std::hash<int> directly.
+//
+// Possible use:
+//
+// struct Xyzzy {
+// int x;
+// int y;
+// string id;
+// };
+//
+// #ifndef SWIG
+// template<> struct XyzzyHash<Xyzzy> {
+// size_t operator()(const Xyzzy& c) const {
+// HashMix mix(hash<int>()(c.x));
+// mix.Mix(hash<int>()(c.y));
+// mix.Mix(GoodFastHash<string>()(c.id));
+// return mix.get();
+// }
+// }
+// #endif
+//
+// HashMix is a lower level interface than std::hash<std::tuple<>>.
+// Use std::hash<std::tuple<>> instead of HashMix where appropriate.
+
+class HashMix {
+ public:
+ HashMix() : hash_(1) {}
+ explicit HashMix(size_t val) : hash_(val + 83) {}
+ void Mix(size_t val) {
+ static const size_t kMul = static_cast<size_t>(0xdc3eb94af8ab4c93ULL);
+ // Multiplicative hashing will mix bits better in the msb end ...
+ hash_ *= kMul;
+ // ... and rotating will move the better mixed msb-bits to lsb-bits.
+ hash_ = ((hash_ << 19) |
+ (hash_ >> (std::numeric_limits<size_t>::digits - 19))) + val;
+ }
+ size_t get() const { return hash_; }
+ private:
+ size_t hash_;
+};
+
+#endif // S2_UTIL_HASH_MIX_H_
--- /dev/null
+// Copyright 2009 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// ExactFloat is a multiple-precision floating point type based on the OpenSSL
+// Bignum library. It has the same interface as the built-in "float" and
+// "double" types, but only supports the subset of operators and intrinsics
+// where it is possible to compute the result exactly. So for example,
+// ExactFloat supports addition and multiplication but not division (since in
+// general, the quotient of two floating-point numbers cannot be represented
+// exactly). Exact arithmetic is useful for geometric algorithms, especially
+// for disambiguating cases where ordinary double-precision arithmetic yields
+// an uncertain result.
+//
+// ExactFloat is a subset of the faster and more capable MPFloat class (which
+// is based on the GNU MPFR library). The main reason to use this class
+// rather than MPFloat is that it is subject to a BSD-style license rather
+// than the much more restrictive LGPL license.
+//
+// It has the following features:
+//
+// - ExactFloat uses the same syntax as the built-in "float" and "double"
+// types, for example: x += 4 + fabs(2*y*y - z*z). There are a few
+// differences (see below), but the syntax is compatible enough so that
+// ExactFloat can be used as a template argument to templatized classes
+// such as Vector2, VectorN, Matrix3x3, etc.
+//
+// - Results are not rounded; instead, precision is increased so that the
+// result can be represented exactly. An inexact result is returned only
+// in the case of underflow or overflow (yielding signed zero or infinity
+// respectively), or if the maximum allowed precision is exceeded (yielding
+// NaN). ExactFloat uses IEEE 754-2008 rules for handling infinities, NaN,
+// rounding to integers, etc.
+//
+// - ExactFloat only supports calculations where the result can be
+// represented exactly. Therefore it supports intrinsics such as fabs()
+// but not transcendentals such as sin(), sqrt(), etc.
+//
+// Syntax Compatibility with "float" and "double"
+// ----------------------------------------------
+//
+// ExactFloat supports a subset of the operators and intrinsics for the
+// built-in "double" type. (Thus it supports fabs() but not fabsf(), for
+// example.) The syntax is different only in the following cases:
+//
+// - Casts and implicit conversions to built-in types (including "bool") are
+// not supported. So for example, the following will not compile:
+//
+// ExactFloat x = 7.5;
+// double y = x; // ERROR: use x.ToDouble() instead
+// long z = x; // ERROR: use x.ToDouble() or lround(trunc(x))
+// q = static_cast<int>(x); // ERROR: use x.ToDouble() or lround(trunc(x))
+// if (x) { ... } // ERROR: use (x != 0) instead
+//
+// - The glibc floating-point classification macros (fpclassify, isfinite,
+// isnormal, isnan, isinf) are not supported. Instead there are
+// zero-argument methods:
+//
+// ExactFloat x;
+// if (isnan(x)) { ... } // ERROR: use (x.is_nan()) instead
+// if (isinf(x)) { ... } // ERROR: use (x.is_inf()) instead
+//
+// Using ExactFloat with Vector3, etc.
+// -----------------------------------
+//
+// ExactFloat can be used with templatized classes such as Vector2 and Vector3
+// (see "util/math/vector.h"), with the following limitations:
+//
+// - Cast() can be used to convert other vector types to an ExactFloat vector
+// type, but not the other way around. This is because there are no
+// implicit conversions from ExactFloat to built-in types. You can work
+// around this by calling an explicit conversion method such as
+// ToDouble(). For example:
+//
+// typedef Vector3<ExactFloat> Vector3_xf;
+// Vector3_xf x;
+// Vector3_d y;
+// x = Vector3_xf::Cast(y); // This works.
+// y = Vector3_d::Cast(x); // This doesn't.
+// y = Vector3_d(x[0].ToDouble(), x[1].ToDouble(), x[2].ToDouble()); // OK
+//
+// - IsNaN() is not supported because it calls isnan(), which is defined as a
+// macro in <math.h> and therefore can't easily be overrided.
+//
+// Precision Semantics
+// -------------------
+//
+// Unlike MPFloat, ExactFloat does not allow a maximum precision to be
+// specified (it is always unbounded). Therefore it does not have any of the
+// corresponding constructors.
+//
+// The current precision of an ExactFloat (i.e., the number of bits in its
+// mantissa) is returned by prec(). The precision is increased as necessary
+// so that the result of every operation can be represented exactly.
+
+#ifndef S2_UTIL_MATH_EXACTFLOAT_EXACTFLOAT_H_
+#define S2_UTIL_MATH_EXACTFLOAT_EXACTFLOAT_H_
+
+#include <algorithm>
+#include <climits>
+#include <cmath>
+#include <algorithm>
+#include <iostream>
+#include <string>
+
+#include <openssl/bn.h>
+
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+#include "s2/base/port.h"
+
+class ExactFloat {
+ public:
+ // The following limits are imposed by OpenSSL.
+
+ // The maximum exponent supported. If a value has an exponent larger than
+ // this, it is replaced by infinity (with the appropriate sign).
+ static const int kMaxExp = 200*1000*1000; // About 10**(60 million)
+
+ // The minimum exponent supported. If a value has an exponent less than
+ // this, it is replaced by zero (with the appropriate sign).
+ static const int kMinExp = -kMaxExp; // About 10**(-60 million)
+
+ // The maximum number of mantissa bits supported. If a value has more
+ // mantissa bits than this, it is replaced with NaN. (It is expected that
+ // users of this class will never want this much precision.)
+ static const int kMaxPrec = 64 << 20; // About 20 million digits
+
+ // Rounding modes. kRoundTiesToEven and kRoundTiesAwayFromZero both round
+ // to the nearest representable value unless two values are equally close.
+ // In that case kRoundTiesToEven rounds to the nearest even value, while
+ // kRoundTiesAwayFromZero always rounds away from zero.
+ enum RoundingMode {
+ kRoundTiesToEven,
+ kRoundTiesAwayFromZero,
+ kRoundTowardZero,
+ kRoundAwayFromZero,
+ kRoundTowardPositive,
+ kRoundTowardNegative
+ };
+
+ /////////////////////////////////////////////////////////////////////////////
+ // Constructors
+
+ // The default constructor initializes the value to zero. (The initial
+ // value must be zero rather than NaN for compatibility with the built-in
+ // float types.)
+ inline ExactFloat();
+
+ // Construct an ExactFloat from a "double". The constructor is implicit so
+ // that this class can be used as a replacement for "float" or "double" in
+ // templatized libraries. (With an explicit constructor, code such as
+ // "ExactFloat f = 2.5;" would not compile.) All double-precision values are
+ // supported, including denormalized numbers, infinities, and NaNs.
+ ExactFloat(double v);
+
+ // Construct an ExactFloat from an "int". Note that in general, ints are
+ // automatically converted to doubles and so would be handled by the
+ // constructor above. However, the particular argument (0) would be
+ // ambiguous; the compiler wouldn't know whether to treat it as a "double" or
+ // "const char*" (since 0 is a valid null pointer constant). Adding an "int"
+ // constructor solves this problem.
+ //
+ // We do not provide constructors for "unsigned", "long", "unsigned long",
+ // "long long", or "unsigned long long", since these types are not typically
+ // used in floating-point calculations and it is safer to require them to be
+ // explicitly cast.
+ ExactFloat(int v);
+
+ // Construct an ExactFloat from a string (such as "1.2e50"). Requires that
+ // the value is exactly representable as a floating-point number (so for
+ // example, "0.125" is allowed but "0.1" is not).
+ explicit ExactFloat(const char* s) { Unimplemented(); }
+
+ // Copy constructor.
+ ExactFloat(const ExactFloat& b);
+
+ // The destructor is not virtual for efficiency reasons. Therefore no
+ // subclass should declare additional fields that require destruction.
+ inline ~ExactFloat() = default;
+
+ /////////////////////////////////////////////////////////////////////
+ // Constants
+ //
+ // As an alternative to the constants below, you can also just use the
+ // constants defined in <math.h>, for example:
+ //
+ // ExactFloat x = NAN, y = -INFINITY;
+
+ // Return an ExactFloat equal to positive zero (if sign >= 0) or
+ // negative zero (if sign < 0).
+ static ExactFloat SignedZero(int sign);
+
+ // Return an ExactFloat equal to positive infinity (if sign >= 0) or
+ // negative infinity (if sign < 0).
+ static ExactFloat Infinity(int sign);
+
+ // Return an ExactFloat that is NaN (Not-a-Number).
+ static ExactFloat NaN();
+
+ /////////////////////////////////////////////////////////////////////////////
+ // Accessor Methods
+
+ // Return the maximum precision of the ExactFloat. This method exists only
+ // for compatibility with MPFloat.
+ int max_prec() const { return kMaxPrec; }
+
+ // Return the actual precision of this ExactFloat (the current number of
+ // bits in its mantissa). Returns 0 for non-normal numbers such as NaN.
+ int prec() const;
+
+ // Return the exponent of this ExactFloat given that the mantissa is in the
+ // range [0.5, 1). It is an error to call this method if the value is zero,
+ // infinity, or NaN.
+ int exp() const;
+
+ // Set the value of the ExactFloat to +0 (if sign >= 0) or -0 (if sign < 0).
+ void set_zero(int sign);
+
+ // Set the value of the ExactFloat to positive infinity (if sign >= 0) or
+ // negative infinity (if sign < 0).
+ void set_inf(int sign);
+
+ // Set the value of the ExactFloat to NaN (Not-a-Number).
+ void set_nan();
+
+ // Unfortunately, isinf(x), isnan(x), isnormal(x), and isfinite(x) are
+ // defined as macros in <math.h>. Therefore we can't easily extend them
+ // here. Instead we provide methods with underscores in their names that do
+ // the same thing: x.is_inf(), etc.
+ //
+ // These macros are not implemented: signbit(x), fpclassify(x).
+
+ // Return true if this value is zero (including negative zero).
+ inline bool is_zero() const;
+
+ // Return true if this value is infinity (positive or negative).
+ inline bool is_inf() const;
+
+ // Return true if this value is NaN (Not-a-Number).
+ inline bool is_nan() const;
+
+ // Return true if this value is a normal floating-point number. Non-normal
+ // values (zero, infinity, and NaN) often need to be handled separately
+ // because they are represented using special exponent values and their
+ // mantissa is not defined.
+ inline bool is_normal() const;
+
+ // Return true if this value is a normal floating-point number or zero,
+ // i.e. it is not infinity or NaN.
+ inline bool is_finite() const;
+
+ // Return true if the sign bit is set (this includes negative zero).
+ inline bool sign_bit() const;
+
+ // Return +1 if this ExactFloat is positive, -1 if it is negative, and 0
+ // if it is zero or NaN. Note that unlike sign_bit(), sgn() returns 0 for
+ // both positive and negative zero.
+ inline int sgn() const;
+
+ /////////////////////////////////////////////////////////////////////////////
+ // Conversion Methods
+ //
+ // Note that some conversions are defined as functions further below,
+ // e.g. to convert to an integer you can use lround(), llrint(), etc.
+
+ // Round to double precision. Note that since doubles have a much smaller
+ // exponent range than ExactFloats, very small values may be rounded to
+ // (positive or negative) zero, and very large values may be rounded to
+ // infinity.
+ //
+ // It is very important to make this a named method rather than an implicit
+ // conversion, because otherwise there would be a silent loss of precision
+ // whenever some desired operator or function happens not to be implemented.
+ // For example, if fabs() were not implemented and "x" and "y" were
+ // ExactFloats, then x = fabs(y) would silently convert "y" to a "double",
+ // take its absolute value, and convert it back to an ExactFloat.
+ double ToDouble() const;
+
+ // Return a human-readable string such that if two values with the same
+ // precision are different, then their string representations are different.
+ // The format is similar to printf("%g"), except that the number of
+ // significant digits depends on the precision (with a minimum of 10).
+ // Trailing zeros are stripped (just like "%g").
+ //
+ // Note that if two values have different precisions, they may have the same
+ // ToString() value even though their values are slightly different. If you
+ // need to distinguish such values, use ToUniqueString() intead.
+ string ToString() const;
+
+ // Return a string formatted according to printf("%Ng") where N is the given
+ // maximum number of significant digits.
+ string ToStringWithMaxDigits(int max_digits) const;
+
+ // Return a human-readable string such that if two ExactFloats have different
+ // values, then their string representations are always different. This
+ // method is useful for debugging. The string has the form "value<prec>",
+ // where "prec" is the actual precision of the ExactFloat (e.g., "0.215<50>").
+ string ToUniqueString() const;
+
+ // Return an upper bound on the number of significant digits required to
+ // distinguish any two floating-point numbers with the given precision when
+ // they are formatted as decimal strings in exponential format.
+ static int NumSignificantDigitsForPrec(int prec);
+
+ // Output the ExactFloat in human-readable format, e.g. for logging.
+ friend std::ostream& operator<<(std::ostream& o, ExactFloat const& f) {
+ return o << f.ToString();
+ }
+
+ /////////////////////////////////////////////////////////////////////////////
+ // Other Methods
+
+ // Round the ExactFloat so that its mantissa has at most "max_prec" bits
+ // using the given rounding mode. Requires "max_prec" to be at least 2
+ // (since kRoundTiesToEven doesn't make sense with fewer bits than this).
+ ExactFloat RoundToMaxPrec(int max_prec, RoundingMode mode) const;
+
+ /////////////////////////////////////////////////////////////////////////////
+ // Operators
+
+ // Assignment operator.
+ ExactFloat& operator=(const ExactFloat& b);
+
+ // Unary plus.
+ ExactFloat operator+() const { return *this; }
+
+ // Unary minus.
+ ExactFloat operator-() const;
+
+ // Addition.
+ friend ExactFloat operator+(const ExactFloat& a, const ExactFloat& b);
+
+ // Subtraction.
+ friend ExactFloat operator-(const ExactFloat& a, const ExactFloat& b);
+
+ // Multiplication.
+ friend ExactFloat operator*(const ExactFloat& a, const ExactFloat& b);
+
+ // Division is not implemented because the result cannot be represented
+ // exactly in general. Doing this properly would require extending all the
+ // operations to support rounding to a specified precision.
+
+ // Arithmetic assignment operators (+=, -=, *=).
+ ExactFloat& operator+=(const ExactFloat& b) { return (*this = *this + b); }
+ ExactFloat& operator-=(const ExactFloat& b) { return (*this = *this - b); }
+ ExactFloat& operator*=(const ExactFloat& b) { return (*this = *this * b); }
+
+ // Comparison operators (==, !=, <, <=, >, >=).
+ friend bool operator==(const ExactFloat& a, const ExactFloat& b);
+ friend bool operator<(const ExactFloat& a, const ExactFloat& b);
+ // These don't need to be friends but are declared here for completeness.
+ inline friend bool operator!=(const ExactFloat& a, const ExactFloat& b);
+ inline friend bool operator<=(const ExactFloat& a, const ExactFloat& b);
+ inline friend bool operator>(const ExactFloat& a, const ExactFloat& b);
+ inline friend bool operator>=(const ExactFloat& a, const ExactFloat& b);
+
+ /////////////////////////////////////////////////////////////////////
+ // Math Intrinsics
+ //
+ // The math intrinsics currently supported by ExactFloat are listed below.
+ // Except as noted, they behave identically to the usual glibc intrinsics
+ // except that they have greater precision. See the man pages for more
+ // information.
+
+ //////// Miscellaneous simple arithmetic functions.
+
+ // Absolute value.
+ friend ExactFloat fabs(const ExactFloat& a);
+ friend ExactFloat abs(const ExactFloat& a);
+
+ // Maximum of two values.
+ friend ExactFloat fmax(const ExactFloat& a, const ExactFloat& b);
+
+ // Minimum of two values.
+ friend ExactFloat fmin(const ExactFloat& a, const ExactFloat& b);
+
+ // Positive difference: max(a - b, 0).
+ friend ExactFloat fdim(const ExactFloat& a, const ExactFloat& b);
+
+ //////// Integer rounding functions that return ExactFloat values.
+
+ // Round up to the nearest integer.
+ friend ExactFloat ceil(const ExactFloat& a);
+
+ // Round down to the nearest integer.
+ friend ExactFloat floor(const ExactFloat& a);
+
+ // Round to the nearest integer not larger in absolute value.
+ // For example: f(-1.9) = -1, f(2.9) = 2.
+ friend ExactFloat trunc(const ExactFloat& a);
+
+ // Round to the nearest integer, rounding halfway cases away from zero.
+ // For example: f(-0.5) = -1, f(0.5) = 1, f(1.5) = 2, f(2.5) = 3.
+ friend ExactFloat round(const ExactFloat& a);
+
+ // Round to the nearest integer, rounding halfway cases to an even integer.
+ // For example: f(-0.5) = 0, f(0.5) = 0, f(1.5) = 2, f(2.5) = 2.
+ friend ExactFloat rint(const ExactFloat& a);
+
+ // A synonym for rint().
+ friend ExactFloat nearbyint(const ExactFloat& a) { return rint(a); }
+
+ //////// Integer rounding functions that return C++ integer types.
+
+ // Like rint(), but rounds to the nearest "long" value. Returns the
+ // minimum/maximum possible integer if the value is out of range.
+ friend long lrint(const ExactFloat& a);
+
+ // Like rint(), but rounds to the nearest "long long" value. Returns the
+ // minimum/maximum possible integer if the value is out of range.
+ friend long long llrint(const ExactFloat& a);
+
+ // Like round(), but rounds to the nearest "long" value. Returns the
+ // minimum/maximum possible integer if the value is out of range.
+ friend long lround(const ExactFloat& a);
+
+ // Like round(), but rounds to the nearest "long long" value. Returns the
+ // minimum/maximum possible integer if the value is out of range.
+ friend long long llround(const ExactFloat& a);
+
+ //////// Remainder functions.
+
+ // The remainder of dividing "a" by "b", where the quotient is rounded toward
+ // zero to the nearest integer. Similar to (a - trunc(a / b) * b).
+ friend ExactFloat fmod(const ExactFloat& a, const ExactFloat& b) {
+ // Note that it is possible to implement this operation exactly, it just
+ // hasn't been done.
+ return Unimplemented();
+ }
+
+ // The remainder of dividing "a" by "b", where the quotient is rounded to the
+ // nearest integer, rounding halfway cases to an even integer. Similar to
+ // (a - rint(a / b) * b).
+ friend ExactFloat remainder(const ExactFloat& a, const ExactFloat& b) {
+ // Note that it is possible to implement this operation exactly, it just
+ // hasn't been done.
+ return Unimplemented();
+ }
+
+ // A synonym for remainder().
+ friend ExactFloat drem(const ExactFloat& a, const ExactFloat& b) {
+ return remainder(a, b);
+ }
+
+ // Break the argument "a" into integer and fractional parts, each of which
+ // has the same sign as "a". The fractional part is returned, and the
+ // integer part is stored in the output parameter "i_ptr". Both output
+ // values are set to have the same maximum precision as "a".
+ friend ExactFloat modf(const ExactFloat& a, ExactFloat* i_ptr) {
+ // Note that it is possible to implement this operation exactly, it just
+ // hasn't been done.
+ return Unimplemented();
+ }
+
+ //////// Floating-point manipulation functions.
+
+ // Return an ExactFloat with the magnitude of "a" and the sign bit of "b".
+ // (Note that an IEEE zero can be either positive or negative.)
+ friend ExactFloat copysign(const ExactFloat& a, const ExactFloat& b);
+
+ // Convert "a" to a normalized fraction in the range [0.5, 1) times a power
+ // of two. Return the fraction and set "exp" to the exponent. If "a" is
+ // zero, infinity, or NaN then return "a" and set "exp" to zero.
+ friend ExactFloat frexp(const ExactFloat& a, int* exp);
+
+ // Return "a" multiplied by 2 raised to the power "exp".
+ friend ExactFloat ldexp(const ExactFloat& a, int exp);
+
+ // A synonym for ldexp().
+ friend ExactFloat scalbn(const ExactFloat& a, int exp) {
+ return ldexp(a, exp);
+ }
+
+ // A version of ldexp() where "exp" is a long integer.
+ friend ExactFloat scalbln(const ExactFloat& a, long exp);
+
+ // Convert "a" to a normalized fraction in the range [1,2) times a power of
+ // two, and return the exponent value as an integer. This is equivalent to
+ // lrint(floor(log2(fabs(a)))) but it is computed more efficiently. Returns
+ // the constants documented in the man page for zero, infinity, or NaN.
+ friend int ilogb(const ExactFloat& a);
+
+ // Convert "a" to a normalized fraction in the range [1,2) times a power of
+ // two, and return the exponent value as an ExactFloat. This is equivalent to
+ // floor(log2(fabs(a))) but it is computed more efficiently.
+ friend ExactFloat logb(const ExactFloat& a);
+
+ protected:
+ // OpenSSL >= 1.1 does not have BN_init, and does not support stack-
+ // allocated BIGNUMS. We use BN_init when possible, but BN_new otherwise.
+ // If the performance penalty is too high, an object pool can be added
+ // in the future.
+#if defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER < 0x10100000L
+ // BoringSSL and OpenSSL < 1.1 support stack allocated BIGNUMs and BN_init.
+ class BigNum {
+ public:
+ BigNum() { BN_init(&bn_); }
+ // Prevent accidental, expensive, copying.
+ BigNum(const BigNum&) = delete;
+ BigNum& operator=(const BigNum&) = delete;
+ ~BigNum() { BN_free(&bn_); }
+ BIGNUM* get() { return &bn_; }
+ const BIGNUM* get() const { return &bn_; }
+ private:
+ BIGNUM bn_;
+ };
+#else
+ class BigNum {
+ public:
+ BigNum() : bn_(BN_new()) {}
+ BigNum(const BigNum&) = delete;
+ BigNum& operator=(const BigNum&) = delete;
+ ~BigNum() { BN_free(bn_); }
+ BIGNUM* get() { return bn_; }
+ const BIGNUM* get() const { return bn_; }
+ private:
+ BIGNUM* bn_;
+ };
+#endif
+
+ // Non-normal numbers are represented using special exponent values and a
+ // mantissa of zero. Do not change these values; methods such as
+ // is_normal() make assumptions about their ordering. Non-normal numbers
+ // can have either a positive or negative sign (including zero and NaN).
+ static const int32 kExpNaN = INT_MAX;
+ static const int32 kExpInfinity = INT_MAX - 1;
+ static const int32 kExpZero = INT_MAX - 2;
+
+ // Normal numbers are represented as (sign_ * bn_ * (2 ** bn_exp_)), where:
+ // - sign_ is either +1 or -1
+ // - bn_ is a BIGNUM with a positive value
+ // - bn_exp_ is the base-2 exponent applied to bn_.
+ int32 sign_;
+ int32 bn_exp_;
+ BigNum bn_;
+
+ // A standard IEEE "double" has a 53-bit mantissa consisting of a 52-bit
+ // fraction plus an implicit leading "1" bit.
+ static const int kDoubleMantissaBits = 53;
+
+ // Convert an ExactFloat with no more than 53 bits in its mantissa to a
+ // "double". This method handles non-normal values (NaN, etc).
+ double ToDoubleHelper() const;
+
+ // Round an ExactFloat so that it is a multiple of (2 ** bit_exp), using the
+ // given rounding mode.
+ ExactFloat RoundToPowerOf2(int bit_exp, RoundingMode mode) const;
+
+ // Convert the ExactFloat to a decimal value of the form 0.ddd * (10 ** x),
+ // with at most "max_digits" significant digits (trailing zeros are removed).
+ // Set (*digits) to the ASCII digits and return the decimal exponent "x".
+ int GetDecimalDigits(int max_digits, string* digits) const;
+
+ // Return a_sign * fabs(a) + b_sign * fabs(b). Used to implement addition
+ // and subtraction.
+ static ExactFloat SignedSum(int a_sign, const ExactFloat* a,
+ int b_sign, const ExactFloat* b);
+
+ // Convert an ExactFloat to its canonical form. Underflow results in signed
+ // zero, overflow results in signed infinity, and precision overflow results
+ // in NaN. A zero mantissa is converted to the canonical zero value with
+ // the given sign; otherwise the mantissa is normalized so that its low bit
+ // is 1. Non-normal numbers are left unchanged.
+ void Canonicalize();
+
+ // Scale the mantissa of this ExactFloat so that it has the same bn_exp_ as
+ // "b", then return -1, 0, or 1 according to whether the scaled mantissa is
+ // less, equal, or greater than the mantissa of "b". Requires that both
+ // values are normal.
+ int ScaleAndCompare(const ExactFloat& b) const;
+
+ // Return true if the magnitude of this ExactFloat is less than the
+ // magnitude of "b". Requires that neither value is NaN.
+ bool UnsignedLess(const ExactFloat& b) const;
+
+ // Return an ExactFloat with the magnitude of this ExactFloat and the given
+ // sign. (Similar to copysign, except that the sign is given explicitly
+ // rather than being copied from another ExactFloat.)
+ inline ExactFloat CopyWithSign(int sign) const;
+
+ // Convert an ExactFloat to an integer of type "T" using the given rounding
+ // mode. The type "T" must be signed. Returns the largest possible integer
+ // for NaN, and clamps out of range values to the largest or smallest
+ // possible values.
+ template <class T> T ToInteger(RoundingMode mode) const;
+
+ // Log a fatal error message (used for unimplemented methods).
+ static ExactFloat Unimplemented();
+};
+
+/////////////////////////////////////////////////////////////////////////
+// Implementation details follow:
+
+inline ExactFloat::ExactFloat() : sign_(1), bn_exp_(kExpZero) {
+}
+
+inline bool ExactFloat::is_zero() const { return bn_exp_ == kExpZero; }
+inline bool ExactFloat::is_inf() const { return bn_exp_ == kExpInfinity; }
+inline bool ExactFloat::is_nan() const { return bn_exp_ == kExpNaN; }
+inline bool ExactFloat::is_normal() const { return bn_exp_ < kExpZero; }
+inline bool ExactFloat::is_finite() const { return bn_exp_ <= kExpZero; }
+inline bool ExactFloat::sign_bit() const { return sign_ < 0; }
+
+inline int ExactFloat::sgn() const {
+ return (is_nan() || is_zero()) ? 0 : sign_;
+}
+
+inline bool operator!=(const ExactFloat& a, const ExactFloat& b) {
+ return !(a == b);
+}
+
+inline bool operator<=(const ExactFloat& a, const ExactFloat& b) {
+ // NaN is unordered compared to everything, including itself.
+ if (a.is_nan() || b.is_nan()) return false;
+ return !(b < a);
+}
+
+inline bool operator>(const ExactFloat& a, const ExactFloat& b) {
+ return b < a;
+}
+
+inline bool operator>=(const ExactFloat& a, const ExactFloat& b) {
+ return b <= a;
+}
+
+inline ExactFloat ExactFloat::CopyWithSign(int sign) const {
+ ExactFloat r = *this;
+ r.sign_ = sign;
+ return r;
+}
+
+#endif // S2_UTIL_MATH_EXACTFLOAT_EXACTFLOAT_H_
--- /dev/null
+// Copyright 2001 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+//
+// This class is intended to contain a collection of useful (static)
+// mathematical functions, properly coded (by consulting numerical
+// recipes or another authoritative source first).
+
+#ifndef S2_UTIL_MATH_MATHUTIL_H_
+#define S2_UTIL_MATH_MATHUTIL_H_
+
+#include <type_traits>
+
+#include "s2/base/integral_types.h"
+
+class MathUtil {
+ public:
+ // Solves for the real roots of x^3+ax^2+bx+c=0, returns true iff
+ // all three are real, in which case the roots are stored (in any
+ // order) in r1, r2, r3; otherwise, exactly one real root exists and
+ // it is stored in r1.
+ static bool RealRootsForCubic(long double a,
+ long double b,
+ long double c,
+ long double *r1,
+ long double *r2,
+ long double *r3);
+
+ // --------------------------------------------------------------------
+ // Round
+ // This function rounds a floating-point number to an integer. It
+ // works for positive or negative numbers.
+ //
+ // Values that are halfway between two integers may be rounded up or
+ // down, for example Round<int>(0.5) == 0 and Round<int>(1.5) == 2.
+ // This allows the function to be implemented efficiently on Intel
+ // processors (see the template specializations at the bottom of this
+ // file). You should not use this function if you care about which
+ // way such half-integers are rounded.
+ //
+ // Example usage:
+ // double y, z;
+ // int x = Round<int>(y + 3.7);
+ // int64 b = Round<int64>(0.3 * z);
+ //
+ // Note that the floating-point template parameter is typically inferred
+ // from the argument type, i.e. there is no need to specify it explicitly.
+ // --------------------------------------------------------------------
+ template <class IntOut, class FloatIn>
+ static IntOut Round(FloatIn x) {
+ static_assert(!std::is_integral<FloatIn>::value, "FloatIn is integer");
+ static_assert(std::is_integral<IntOut>::value, "IntOut is not integer");
+
+ // We don't use sgn(x) below because there is no need to distinguish the
+ // (x == 0) case. Also note that there are specialized faster versions
+ // of this function for Intel processors at the bottom of this file.
+ return static_cast<IntOut>(x < 0 ? (x - 0.5) : (x + 0.5));
+ }
+
+ // --------------------------------------------------------------------
+ // FastIntRound, FastInt64Round
+ // Fast routines for converting floating-point numbers to integers.
+ //
+ // These routines are approximately 6 times faster than the default
+ // implementation of Round<int> on Intel processors (12 times faster on
+ // the Pentium 3). They are also more than 5 times faster than simply
+ // casting a "double" to an "int" using static_cast<int>. This is
+ // because casts are defined to truncate towards zero, which on Intel
+ // processors requires changing the rounding mode and flushing the
+ // floating-point pipeline (unless programs are compiled specifically
+ // for the Pentium 4, which has a new instruction to avoid this).
+ //
+ // Numbers that are halfway between two integers may be rounded up or
+ // down. This is because the conversion is done using the default
+ // rounding mode, which rounds towards the closest even number in case
+ // of ties. So for example, FastIntRound(0.5) == 0, but
+ // FastIntRound(1.5) == 2. These functions should only be used with
+ // applications that don't care about which way such half-integers are
+ // rounded.
+ //
+ // There are template specializations of Round() which call these
+ // functions (for "int" and "int64" only), but it's safer to call them
+ // directly.
+ //
+ // This functions are equivalent to lrint() and llrint() as defined in
+ // the ISO C99 standard. Unfortunately this standard does not seem to
+ // widely adopted yet and these functions are not available by default.
+ // --------------------------------------------------------------------
+
+ static int32 FastIntRound(double x) {
+ // This function is not templatized because gcc doesn't seem to be able
+ // to deal with inline assembly code in templatized functions, and there
+ // is no advantage to passing an argument type of "float" on Intel
+ // architectures anyway.
+
+#if defined __GNUC__ && (defined __i386__ || defined __SSE2__)
+#if defined __SSE2__
+ // SSE2.
+ int32 result;
+ __asm__ __volatile__
+ ("cvtsd2si %1, %0"
+ : "=r" (result) // Output operand is a register
+ : "x" (x)); // Input operand is an xmm register
+ return result;
+#elif defined __i386__
+ // FPU stack. Adapted from /usr/include/bits/mathinline.h.
+ int32 result;
+ __asm__ __volatile__
+ ("fistpl %0"
+ : "=m" (result) // Output operand is a memory location
+ : "t" (x) // Input operand is top of FP stack
+ : "st"); // Clobbers (pops) top of FP stack
+ return result;
+#endif // if defined __x86_64__ || ...
+#else
+ return Round<int32, double>(x);
+#endif // if defined __GNUC__ && ...
+ }
+
+ static int64 FastInt64Round(double x) {
+#if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
+#if defined __x86_64__
+ // SSE2.
+ int64 result;
+ __asm__ __volatile__
+ ("cvtsd2si %1, %0"
+ : "=r" (result) // Output operand is a register
+ : "x" (x)); // Input operand is an xmm register
+ return result;
+#elif defined __i386__
+ // There is no CVTSD2SI in i386 to produce a 64 bit int, even with SSE2.
+ // FPU stack. Adapted from /usr/include/bits/mathinline.h.
+ int64 result;
+ __asm__ __volatile__
+ ("fistpll %0"
+ : "=m" (result) // Output operand is a memory location
+ : "t" (x) // Input operand is top of FP stack
+ : "st"); // Clobbers (pops) top of FP stack
+ return result;
+#endif // if defined __i386__
+#else
+ return Round<int64, double>(x);
+#endif // if defined __GNUC__ && ...
+ }
+};
+
+// ========================================================================= //
+
+#if (defined __i386__ || defined __x86_64__) && defined __GNUC__
+
+// We define template specializations of Round() to get the more efficient
+// Intel versions when possible. Note that gcc does not currently support
+// partial specialization of templatized functions.
+
+template<>
+inline int32 MathUtil::Round<int32, double>(double x) {
+ return FastIntRound(x);
+}
+
+template<>
+inline int32 MathUtil::Round<int32, float>(float x) {
+ return FastIntRound(x);
+}
+
+template<>
+inline int64 MathUtil::Round<int64, double>(double x) {
+ return FastInt64Round(x);
+}
+
+template<>
+inline int64 MathUtil::Round<int64, float>(float x) {
+ return FastInt64Round(x);
+}
+
+#endif
+
+#endif // S2_UTIL_MATH_MATHUTIL_H_
--- /dev/null
+// Copyright 2003 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+//
+// A simple class to handle 3x3 matrices
+// The aim of this class is to be able to manipulate 3x3 matrices
+// and 3D vectors as naturally as possible and make calculations
+// readable.
+// For that reason, the operators +, -, * are overloaded.
+// (Reading a = a + b*2 - c is much easier to read than
+// a = Sub(Add(a, Mul(b,2)),c) )
+//
+// Please be careful about overflows when using those matrices wth integer types
+// The calculations are carried with VType. eg : if you are using uint8 as the
+// base type, all values will be modulo 256.
+// This feature is necessary to use the class in a more general framework with
+// VType != plain old data type.
+
+#ifndef S2_UTIL_MATH_MATRIX3X3_H_
+#define S2_UTIL_MATH_MATRIX3X3_H_
+
+#include <cmath>
+#include <iosfwd>
+#include <type_traits>
+
+#include "s2/base/logging.h"
+#include "s2/util/math/mathutil.h"
+#include "s2/util/math/vector.h"
+
+template <class VType>
+class Matrix3x3 {
+ private:
+ VType m_[3][3];
+
+ public:
+ typedef Matrix3x3<VType> Self;
+ typedef VType BaseType;
+ typedef Vector3<VType> MVector;
+
+ // Initialize the matrix to 0
+ Matrix3x3() {
+ m_[0][2] = m_[0][1] = m_[0][0] = VType();
+ m_[1][2] = m_[1][1] = m_[1][0] = VType();
+ m_[2][2] = m_[2][1] = m_[2][0] = VType();
+ }
+
+ // Constructor explicitly setting the values of all the coefficient of
+ // the matrix
+ Matrix3x3(const VType &m00, const VType &m01, const VType &m02,
+ const VType &m10, const VType &m11, const VType &m12,
+ const VType &m20, const VType &m21, const VType &m22) {
+ m_[0][0] = m00;
+ m_[0][1] = m01;
+ m_[0][2] = m02;
+
+ m_[1][0] = m10;
+ m_[1][1] = m11;
+ m_[1][2] = m12;
+
+ m_[2][0] = m20;
+ m_[2][1] = m21;
+ m_[2][2] = m22;
+ }
+
+ // Casting constructor
+ template <class VType2>
+ static Matrix3x3 Cast(const Matrix3x3<VType2> &mb) {
+ return Matrix3x3(static_cast<VType>(mb(0, 0)),
+ static_cast<VType>(mb(0, 1)),
+ static_cast<VType>(mb(0, 2)),
+ static_cast<VType>(mb(1, 0)),
+ static_cast<VType>(mb(1, 1)),
+ static_cast<VType>(mb(1, 2)),
+ static_cast<VType>(mb(2, 0)),
+ static_cast<VType>(mb(2, 1)),
+ static_cast<VType>(mb(2, 2)));
+ }
+
+ // Change the value of all the coefficients of the matrix
+ inline Matrix3x3 &
+ Set(const VType &m00, const VType &m01, const VType &m02,
+ const VType &m10, const VType &m11, const VType &m12,
+ const VType &m20, const VType &m21, const VType &m22) {
+ m_[0][0] = m00;
+ m_[0][1] = m01;
+ m_[0][2] = m02;
+
+ m_[1][0] = m10;
+ m_[1][1] = m11;
+ m_[1][2] = m12;
+
+ m_[2][0] = m20;
+ m_[2][1] = m21;
+ m_[2][2] = m22;
+ return (*this);
+ }
+
+ // Matrix addition
+ inline Matrix3x3& operator+=(const Matrix3x3 &mb) {
+ m_[0][0] += mb.m_[0][0];
+ m_[0][1] += mb.m_[0][1];
+ m_[0][2] += mb.m_[0][2];
+
+ m_[1][0] += mb.m_[1][0];
+ m_[1][1] += mb.m_[1][1];
+ m_[1][2] += mb.m_[1][2];
+
+ m_[2][0] += mb.m_[2][0];
+ m_[2][1] += mb.m_[2][1];
+ m_[2][2] += mb.m_[2][2];
+ return (*this);
+ }
+
+ // Matrix subtration
+ inline Matrix3x3& operator-=(const Matrix3x3 &mb) {
+ m_[0][0] -= mb.m_[0][0];
+ m_[0][1] -= mb.m_[0][1];
+ m_[0][2] -= mb.m_[0][2];
+
+ m_[1][0] -= mb.m_[1][0];
+ m_[1][1] -= mb.m_[1][1];
+ m_[1][2] -= mb.m_[1][2];
+
+ m_[2][0] -= mb.m_[2][0];
+ m_[2][1] -= mb.m_[2][1];
+ m_[2][2] -= mb.m_[2][2];
+ return (*this);
+ }
+
+ // Matrix multiplication by a scalar
+ inline Matrix3x3& operator*=(const VType &k) {
+ m_[0][0] *= k;
+ m_[0][1] *= k;
+ m_[0][2] *= k;
+
+ m_[1][0] *= k;
+ m_[1][1] *= k;
+ m_[1][2] *= k;
+
+ m_[2][0] *= k;
+ m_[2][1] *= k;
+ m_[2][2] *= k;
+ return (*this);
+ }
+
+ // Matrix addition
+ inline Matrix3x3 operator+(const Matrix3x3 &mb) const {
+ return Matrix3x3(*this) += mb;
+ }
+
+ // Matrix subtraction
+ inline Matrix3x3 operator-(const Matrix3x3 &mb) const {
+ return Matrix3x3(*this) -= mb;
+ }
+
+ // Change the sign of all the coefficients in the matrix
+ friend inline Matrix3x3 operator-(const Matrix3x3 &vb) {
+ return Matrix3x3(-vb.m_[0][0], -vb.m_[0][1], -vb.m_[0][2],
+ -vb.m_[1][0], -vb.m_[1][1], -vb.m_[1][2],
+ -vb.m_[2][0], -vb.m_[2][1], -vb.m_[2][2]);
+ }
+
+ // Matrix multiplication by a scalar
+ inline Matrix3x3 operator*(const VType &k) const {
+ return Matrix3x3(*this) *= k;
+ }
+
+ friend inline Matrix3x3 operator*(const VType &k, const Matrix3x3 &mb) {
+ return Matrix3x3(mb)*k;
+ }
+
+ // Matrix multiplication
+ inline Matrix3x3 operator*(const Matrix3x3 &mb) const {
+ return Matrix3x3(
+ m_[0][0] * mb.m_[0][0] + m_[0][1] * mb.m_[1][0] + m_[0][2] * mb.m_[2][0],
+ m_[0][0] * mb.m_[0][1] + m_[0][1] * mb.m_[1][1] + m_[0][2] * mb.m_[2][1],
+ m_[0][0] * mb.m_[0][2] + m_[0][1] * mb.m_[1][2] + m_[0][2] * mb.m_[2][2],
+
+ m_[1][0] * mb.m_[0][0] + m_[1][1] * mb.m_[1][0] + m_[1][2] * mb.m_[2][0],
+ m_[1][0] * mb.m_[0][1] + m_[1][1] * mb.m_[1][1] + m_[1][2] * mb.m_[2][1],
+ m_[1][0] * mb.m_[0][2] + m_[1][1] * mb.m_[1][2] + m_[1][2] * mb.m_[2][2],
+
+ m_[2][0] * mb.m_[0][0] + m_[2][1] * mb.m_[1][0] + m_[2][2] * mb.m_[2][0],
+ m_[2][0] * mb.m_[0][1] + m_[2][1] * mb.m_[1][1] + m_[2][2] * mb.m_[2][1],
+ m_[2][0] * mb.m_[0][2] + m_[2][1] * mb.m_[1][2] + m_[2][2] * mb.m_[2][2]);
+ }
+
+ // Multiplication of a matrix by a vector
+ inline MVector operator*(const MVector &v) const {
+ return MVector(
+ m_[0][0] * v[0] + m_[0][1] * v[1] + m_[0][2] * v[2],
+ m_[1][0] * v[0] + m_[1][1] * v[1] + m_[1][2] * v[2],
+ m_[2][0] * v[0] + m_[2][1] * v[1] + m_[2][2] * v[2]);
+ }
+
+ // Return the determinant of the matrix
+ inline VType Det(void) const {
+ return m_[0][0] * m_[1][1] * m_[2][2]
+ + m_[0][1] * m_[1][2] * m_[2][0]
+ + m_[0][2] * m_[1][0] * m_[2][1]
+ - m_[2][0] * m_[1][1] * m_[0][2]
+ - m_[2][1] * m_[1][2] * m_[0][0]
+ - m_[2][2] * m_[1][0] * m_[0][1];
+ }
+
+ // Return the trace of the matrix
+ inline VType Trace(void) const {
+ return m_[0][0] + m_[1][1] + m_[2][2];
+ }
+
+ // Return a pointer to the data array for interface with other libraries
+ // like opencv
+ VType* Data() {
+ return reinterpret_cast<VType*>(m_);
+ }
+ const VType* Data() const {
+ return reinterpret_cast<const VType*>(m_);
+ }
+
+ // Return matrix element (i,j) with 0<=i<=2 0<=j<=2
+ inline VType &operator()(const int i, const int j) {
+ S2_DCHECK_GE(i, 0);
+ S2_DCHECK_LT(i, 3);
+ S2_DCHECK_GE(j, 0);
+ S2_DCHECK_LT(j, 3);
+ return m_[i][j];
+ }
+ inline VType operator()(const int i, const int j) const {
+ S2_DCHECK_GE(i, 0);
+ S2_DCHECK_LT(i, 3);
+ S2_DCHECK_GE(j, 0);
+ S2_DCHECK_LT(j, 3);
+ return m_[i][j];
+ }
+
+ // Return matrix element (i/3,i%3) with 0<=i<=8 (access concatenated rows).
+ inline VType &operator[](const int i) {
+ S2_DCHECK_GE(i, 0);
+ S2_DCHECK_LT(i, 9);
+ return reinterpret_cast<VType*>(m_)[i];
+ }
+ inline VType operator[](const int i) const {
+ S2_DCHECK_GE(i, 0);
+ S2_DCHECK_LT(i, 9);
+ return reinterpret_cast<const VType*>(m_)[i];
+ }
+
+ // Return the transposed matrix
+ inline Matrix3x3 Transpose(void) const {
+ return Matrix3x3(m_[0][0], m_[1][0], m_[2][0],
+ m_[0][1], m_[1][1], m_[2][1],
+ m_[0][2], m_[1][2], m_[2][2]);
+ }
+
+ // Return the transposed of the matrix of the cofactors
+ // (Useful for inversion for example)
+ inline Matrix3x3 ComatrixTransposed(void) const {
+ return Matrix3x3(
+ m_[1][1] * m_[2][2] - m_[2][1] * m_[1][2],
+ m_[2][1] * m_[0][2] - m_[0][1] * m_[2][2],
+ m_[0][1] * m_[1][2] - m_[1][1] * m_[0][2],
+
+ m_[1][2] * m_[2][0] - m_[2][2] * m_[1][0],
+ m_[2][2] * m_[0][0] - m_[0][2] * m_[2][0],
+ m_[0][2] * m_[1][0] - m_[1][2] * m_[0][0],
+
+ m_[1][0] * m_[2][1] - m_[2][0] * m_[1][1],
+ m_[2][0] * m_[0][1] - m_[0][0] * m_[2][1],
+ m_[0][0] * m_[1][1] - m_[1][0] * m_[0][1]);
+ }
+ // Matrix inversion
+ inline Matrix3x3 Inverse(void) const {
+ VType det = Det();
+ S2_CHECK_NE(det, VType(0)) << " Can't inverse. Determinant = 0.";
+ return (VType(1) / det) * ComatrixTransposed();
+ }
+
+ // Return the vector 3D at row i
+ inline MVector Row(const int i) const {
+ S2_DCHECK_GE(i, 0);
+ S2_DCHECK_LT(i, 3);
+ return MVector(m_[i][0], m_[i][1], m_[i][2]);
+ }
+
+ // Return the vector 3D at col i
+ inline MVector Col(const int i) const {
+ S2_DCHECK_GE(i, 0);
+ S2_DCHECK_LT(i, 3);
+ return MVector(m_[0][i], m_[1][i], m_[2][i]);
+ }
+
+ // Create a matrix from 3 row vectors
+ static inline Matrix3x3 FromRows(const MVector &v1,
+ const MVector &v2,
+ const MVector &v3) {
+ Matrix3x3 temp;
+ temp.Set(v1[0], v1[1], v1[2],
+ v2[0], v2[1], v2[2],
+ v3[0], v3[1], v3[2]);
+ return temp;
+ }
+
+ // Create a matrix from 3 column vectors
+ static inline Matrix3x3 FromCols(const MVector &v1,
+ const MVector &v2,
+ const MVector &v3) {
+ Matrix3x3 temp;
+ temp.Set(v1[0], v2[0], v3[0],
+ v1[1], v2[1], v3[1],
+ v1[2], v2[2], v3[2]);
+ return temp;
+ }
+
+ // Set the vector in row i to be v1
+ void SetRow(int i, const MVector &v1) {
+ S2_DCHECK_GE(i, 0);
+ S2_DCHECK_LT(i, 3);
+ m_[i][0] = v1[0];
+ m_[i][1] = v1[1];
+ m_[i][2] = v1[2];
+ }
+
+ // Set the vector in column i to be v1
+ void SetCol(int i, const MVector &v1) {
+ S2_DCHECK_GE(i, 0);
+ S2_DCHECK_LT(i, 3);
+ m_[0][i] = v1[0];
+ m_[1][i] = v1[1];
+ m_[2][i] = v1[2];
+ }
+
+ // Return a matrix M close to the original but verifying MtM = I
+ // (useful to compensate for errors in a rotation matrix)
+ Matrix3x3 Orthogonalize() const {
+ MVector r1, r2, r3;
+ r1 = Row(0).Normalize();
+ r2 = (Row(2).CrossProd(r1)).Normalize();
+ r3 = (r1.CrossProd(r2)).Normalize();
+ return FromRows(r1, r2, r3);
+ }
+
+ // Return the identity matrix
+ static inline Matrix3x3 Identity(void) {
+ Matrix3x3 temp;
+ temp.Set(VType(1), VType(0), VType(0), //
+ VType(0), VType(1), VType(0), //
+ VType(0), VType(0), VType(1));
+ return temp;
+ }
+
+ // Return a matrix full of zeros
+ static inline Matrix3x3 Zero(void) {
+ return Matrix3x3();
+ }
+
+ // Return a diagonal matrix with the coefficients in v
+ static inline Matrix3x3 Diagonal(const MVector &v) {
+ return Matrix3x3(v[0], VType(), VType(),
+ VType(), v[1], VType(),
+ VType(), VType(), v[2]);
+ }
+
+ // Return the matrix vvT
+ static Matrix3x3 Sym3(const MVector &v) {
+ return Matrix3x3(
+ v[0]*v[0], v[0]*v[1], v[0]*v[2],
+ v[1]*v[0], v[1]*v[1], v[1]*v[2],
+ v[2]*v[0], v[2]*v[1], v[2]*v[2]);
+ }
+ // Return a matrix M such that:
+ // for each u, M * u = v.CrossProd(u)
+ static Matrix3x3 AntiSym3(const MVector &v) {
+ return Matrix3x3(VType(), -v[2], v[1],
+ v[2], VType(), -v[0],
+ -v[1], v[0], VType());
+ }
+
+ // Returns matrix that rotates |rot| radians around axis rot.
+ static Matrix3x3 Rodrigues(const MVector &rot) {
+ Matrix3x3 R;
+ VType theta = rot.Norm();
+ MVector w = rot.Normalize();
+ Matrix3x3 Wv = Matrix3x3::AntiSym3(w);
+ Matrix3x3 I = Matrix3x3::Identity();
+ Matrix3x3 A = Matrix3x3::Sym3(w);
+ R = (1 - cos(theta)) * A + sin(theta) * Wv + cos(theta) * I;
+ return R;
+ }
+
+ // Returns v.Transpose() * (*this) * u
+ VType MulBothSides(const MVector &v, const MVector &u) const {
+ return ((*this) * u).DotProd(v);
+ }
+
+ // Use the 3x3 matrix as a projective transform for 2d points
+ Vector2<VType> Project(const Vector2<VType> &v) const {
+ MVector temp = (*this) * MVector(v[0], v[1], 1);
+ return Vector2<VType>(temp[0] / temp[2], temp[1] / temp[2]);
+ }
+
+ // Return the Frobenius norm of the matrix: sqrt(sum(aij^2))
+ VType FrobeniusNorm() const {
+ VType sum = VType();
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ sum += m_[i][j] * m_[i][j];
+ }
+ }
+ return sqrt(sum);
+ }
+
+ // Finds the eigen values of the matrix. Return the number of real eigenvalues
+ // found.
+ // If the matrix is known to be symmetric due to your problem formulation,
+ // then please use SymmetricEigenSolver, since this method does not guarantee
+ // finding all 3 real eigenvalues in pathological cases. See CL 49170250.
+ int EigenValues(MVector *eig_val) const {
+ long double r1, r2, r3; // NOLINT
+ // characteristic polynomial
+ // x^3 + a*x^2 + b*x + c
+ VType a = -Trace();
+ VType b = m_[0][0]*m_[1][1] + m_[1][1]*m_[2][2] + m_[2][2]*m_[0][0]
+ - m_[1][0]*m_[0][1] - m_[2][1]*m_[1][2] - m_[0][2]*m_[2][0];
+ VType c = -Det();
+ bool res = MathUtil::RealRootsForCubic(a, b, c, &r1, &r2, &r3);
+ (*eig_val)[0] = r1;
+ if (res) {
+ (*eig_val)[1] = r2;
+ (*eig_val)[2] = r3;
+ return 3;
+ }
+ return 1;
+ }
+
+ // Finds the eigen values and optional associated eigen vectors of a
+ // symmetric 3x3 matrix (not necessarily positive definite).
+ // eigen values are sorted in decreasing order;
+ // eig_val corresponds to the columns of the eig_vec matrix.
+ // Note: The routine will only use the lower diagonal part
+ // of the matrix, i.e.
+ // | a00, |
+ // | a10, a11, |
+ // | a20, a21, a22 |
+ void SymmetricEigenSolver(MVector *eig_val,
+ Matrix3x3 *eig_vec /*nullable*/) const {
+ // Compute characteristic polynomial coefficients.
+ double c2 = -Trace();
+ double c1 = -(m_[1][0] * m_[1][0] - m_[0][0] * m_[1][1]
+ - m_[0][0] * m_[2][2] - m_[1][1] * m_[2][2]
+ + m_[2][0] * m_[2][0] + m_[2][1] * m_[2][1]);
+ double c0 = -(m_[0][0] * m_[1][1] * m_[2][2] //
+ - m_[2][0] * m_[2][0] * m_[1][1] //
+ - m_[1][0] * m_[1][0] * m_[2][2] //
+ - m_[0][0] * m_[2][1] * m_[2][1] //
+ + 2 * m_[1][0] * m_[2][0] * m_[2][1]);
+
+ // Root finding x^3 + c2*x^2 + c1*x + c0 = 0.
+ // NOTE: Cannot reuse general cubic solver MathUtil::RealRootsForCubic()
+ // because it doesn't guarantee finding 3 real roots, e.g. it won't always
+ // return roots {2, 2, 0} for the cubic x^3 - 4*x^2 + 4*x + epsilon = 0.
+ double q = (c2*c2-3*c1)/9.0;
+ double r = (2*c2*c2*c2-9*c2*c1+27*c0)/54.0;
+ // Assume R^2 <= Q^3 so there are three real roots.
+ // Avoid sqrt of negative q, which can only happen due to numerical error.
+ if (q < 0) q = 0;
+ double sqrt_q = -2.0 * sqrt(q);
+ double q3_r2 = q * q * q - r * r;
+ // Avoid sqrt of negative q3_r2, which can only happen due to numerical
+ // error.
+ double theta = atan2(q3_r2 <= 0 ? 0 : sqrt(q3_r2), r);
+ double c2_3 = c2 / 3;
+ (*eig_val)[0] = sqrt_q * cos(theta / 3.0) - c2_3;
+ (*eig_val)[1] = sqrt_q * cos((theta + 2.0 * M_PI)/3.0) - c2_3;
+ (*eig_val)[2] = sqrt_q * cos((theta - 2.0 * M_PI)/3.0) - c2_3;
+
+ // Sort eigen value in decreasing order
+ Vector3<int> d_order = eig_val->ComponentOrder();
+ (*eig_val) = MVector((*eig_val)[d_order[2]],
+ (*eig_val)[d_order[1]],
+ (*eig_val)[d_order[0]]);
+
+ // Compute eigenvectors
+ if (!eig_vec) return;
+ for (int i = 0; i < 3; ++i) {
+ MVector r1 , r2 , r3 , e1 , e2 , e3;
+ r1[0] = m_[0][0] - (*eig_val)[i];
+ r2[0] = m_[1][0];
+ r3[0] = m_[2][0];
+ r1[1] = m_[1][0];
+ r2[1] = m_[1][1] - (*eig_val)[i];
+ r3[1] = m_[2][1];
+ r1[2] = m_[2][0];
+ r2[2] = m_[2][1];
+ r3[2] = m_[2][2] - (*eig_val)[i];
+
+ e1 = r1.CrossProd(r2);
+ e2 = r2.CrossProd(r3);
+ e3 = r3.CrossProd(r1);
+
+ // Make e2 and e3 point in the same direction as e1
+ if (e2.DotProd(e1) < 0) e2 = -e2;
+ if (e3.DotProd(e1) < 0) e3 = -e3;
+ MVector e = (e1 + e2 + e3).Normalize();
+ eig_vec->SetCol(i, e);
+ }
+ }
+
+ // Return true is one of the elements of the matrix is NaN
+ bool IsNaN() const {
+ for ( int i = 0; i < 3; ++i ) {
+ for ( int j = 0; j < 3; ++j ) {
+ if ( isnan(m_[i][j]) ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ friend bool operator==(const Matrix3x3 &a, const Matrix3x3 &b) {
+ return a.m_[0][0] == b.m_[0][0] &&
+ a.m_[0][1] == b.m_[0][1] &&
+ a.m_[0][2] == b.m_[0][2] &&
+ a.m_[1][0] == b.m_[1][0] &&
+ a.m_[1][1] == b.m_[1][1] &&
+ a.m_[1][2] == b.m_[1][2] &&
+ a.m_[2][0] == b.m_[2][0] &&
+ a.m_[2][1] == b.m_[2][1] &&
+ a.m_[2][2] == b.m_[2][2];
+ }
+
+ friend bool operator!=(const Matrix3x3 &a, const Matrix3x3 &b) {
+ return !(a == b);
+ }
+
+ friend std::ostream &operator <<(std::ostream &out, const Matrix3x3 &mb) {
+ int i, j;
+ for (i = 0; i < 3; i++) {
+ if (i ==0) {
+ out << "[";
+ } else {
+ out << " ";
+ }
+ for (j = 0; j < 3; j++) {
+ out << mb(i, j) << " ";
+ }
+ if (i == 2) {
+ out << "]";
+ } else {
+ out << std::endl;
+ }
+ }
+ return out;
+ }
+};
+
+typedef Matrix3x3<int> Matrix3x3_i;
+typedef Matrix3x3<float> Matrix3x3_f;
+typedef Matrix3x3<double> Matrix3x3_d;
+
+
+#endif // S2_UTIL_MATH_MATRIX3X3_H_
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Simple classes to handle vectors in 2D, 3D, and 4D.
+//
+// Maintainers: Please be mindful of extreme degradations in unoptimized
+// performance here.
+#ifndef S2_UTIL_MATH_VECTOR_H_
+#define S2_UTIL_MATH_VECTOR_H_
+
+#include <algorithm>
+#include <cmath>
+#include <cstdlib>
+#include <iosfwd>
+#include <iostream> // NOLINT(readability/streams)
+#include <limits>
+#include <type_traits>
+
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/third_party/absl/utility/utility.h"
+
+template <typename T> class Vector2;
+template <typename T> class Vector3;
+template <typename T> class Vector4;
+
+namespace util {
+namespace math {
+namespace internal_vector {
+
+// CRTP base class for all Vector templates.
+template <template <typename> class VecTemplate, typename T, std::size_t N>
+class BasicVector {
+ using D = VecTemplate<T>;
+
+ protected:
+ // FloatType is the type returned by Norm() and Angle(). These methods are
+ // special because they return floating-point values even when VType is an
+ // integer.
+ typedef typename std::conditional<std::is_integral<T>::value,
+ double, T>::type FloatType;
+
+ using IdxSeqN = typename absl::make_index_sequence<N>;
+
+ template <std::size_t I, typename F, typename... As>
+ static auto Reduce(F f, As*... as)
+ -> decltype(f(as[I]...)) {
+ return f(as[I]...);
+ }
+
+ template <typename R = D, std::size_t... Is, typename F, typename... As>
+ static R GenerateEach(absl::index_sequence<Is...>, F f, As*... as) {
+ return R(Reduce<Is>(f, as...)...);
+ }
+
+ // Generate<R>(f,a,b,...) returns an R(...), where the constructor arguments
+ // are created as a transform. R(f(a[0],b[0],...), f(a[1],b[1],...), ...),
+ // and with a,b,... all optional.
+ template <typename R = D, typename F, typename... As>
+ static R Generate(F f, As&&... as) {
+ return GenerateEach<R>(IdxSeqN(), f, std::forward<As>(as).Data()...);
+ }
+
+ public:
+ enum { SIZE = N };
+ static int Size() { return SIZE; }
+
+ void Clear() { AsD() = D(); }
+
+ T& operator[](int b) {
+ S2_DCHECK_GE(b, 0);
+ S2_DCHECK_LT(b, SIZE);
+ return static_cast<D&>(*this).Data()[b];
+ }
+ T operator[](int b) const {
+ S2_DCHECK_GE(b, 0);
+ S2_DCHECK_LT(b, SIZE);
+ return static_cast<const D&>(*this).Data()[b];
+ }
+
+ // TODO(user): Relationals should be nonmembers.
+ bool operator==(const D& b) const {
+ const T* ap = static_cast<const D&>(*this).Data();
+ return std::equal(ap, ap + this->Size(), b.Data());
+ }
+ bool operator!=(const D& b) const { return !(AsD() == b); }
+ bool operator<(const D& b) const {
+ const T* ap = static_cast<const D&>(*this).Data();
+ const T* bp = b.Data();
+ return std::lexicographical_compare(
+ ap, ap + this->Size(), bp, bp + b.Size());
+ }
+ bool operator>(const D& b) const { return b < AsD(); }
+ bool operator<=(const D& b) const { return !(AsD() > b); }
+ bool operator>=(const D& b) const { return !(AsD() < b); }
+
+ D& operator+=(const D& b) {
+ PlusEq(static_cast<D&>(*this).Data(), b.Data(), IdxSeqN{});
+ return static_cast<D&>(*this);
+ }
+
+ D& operator-=(const D& b) {
+ MinusEq(static_cast<D&>(*this).Data(), b.Data(), IdxSeqN{});
+ return static_cast<D&>(*this);
+ }
+
+ D& operator*=(T k) {
+ MulEq(static_cast<D&>(*this).Data(), k, IdxSeqN{});
+ return static_cast<D&>(*this);
+ }
+
+ D& operator/=(T k) {
+ DivEq(static_cast<D&>(*this).Data(), k, IdxSeqN{});
+ return static_cast<D&>(*this);
+ }
+
+ D operator+(const D& b) const { return D(AsD()) += b; }
+ D operator-(const D& b) const { return D(AsD()) -= b; }
+ D operator*(T k) const { return D(AsD()) *= k; }
+ D operator/(T k) const { return D(AsD()) /= k; }
+
+ friend D operator-(const D& a) {
+ return Generate([](const T& x) { return -x; }, a);
+ }
+
+ // Convert from another vector type
+ template <typename T2>
+ static D Cast(const VecTemplate<T2> &b) {
+ return Generate([](const T2& x) { return static_cast<T>(x); }, b);
+ }
+
+ // multiply two vectors component by component
+ D MulComponents(const D &b) const {
+ return Generate([](const T& x, const T& y) { return x * y; }, AsD(), b);
+ }
+ // divide two vectors component by component
+ D DivComponents(const D &b) const {
+ return Generate([](const T& x, const T& y) { return x / y; }, AsD(), b);
+ }
+
+ // Element-wise max. {max(a[0],b[0]), max(a[1],b[1]), ...}
+ friend D Max(const D &a, const D &b) {
+ return Generate([](const T& x, const T& y) {
+ return std::max(x, y);
+ }, a, b);
+ }
+
+ // Element-wise min. {min(a[0],b[0]), min(a[1],b[1]), ...}
+ friend D Min(const D &a, const D &b) {
+ return Generate([](const T& x, const T& y) {
+ return std::min(x, y);
+ }, a, b);
+ }
+
+ T DotProd(const D& b) const {
+ return Dot(static_cast<T>(0), static_cast<const D&>(*this).Data(), b.Data(),
+ IdxSeqN{});
+ }
+
+ // Squared Euclidean norm (the dot product with itself).
+ T Norm2() const { return DotProd(AsD()); }
+
+ // Euclidean norm. For integer T, correct only if Norm2 does not overflow.
+ FloatType Norm() const {
+ using std::sqrt;
+ return sqrt(Norm2());
+ }
+
+ // Normalized vector if the norm is nonzero. Not for integer types.
+ D Normalize() const {
+ static_assert(!std::is_integral<T>::value, "must be floating point");
+ T n = Norm();
+ if (n != T(0.0)) {
+ n = T(1.0) / n;
+ }
+ return D(AsD()) *= n;
+ }
+
+ // Compose a vector from the sqrt of each component.
+ D Sqrt() const {
+ return Generate([](const T& x) {
+ using std::sqrt;
+ return sqrt(x);
+ }, AsD());
+ }
+
+ // Take the floor of each component.
+ D Floor() const {
+ return Generate([](const T& x) { return floor(x); }, AsD());
+ }
+
+ // Take the ceil of each component.
+ D Ceil() const {
+ return Generate([](const T& x) { return ceil(x); }, AsD());
+ }
+
+ // Round of each component.
+ D FRound() const {
+ using std::rint;
+ return Generate([](const T& x) { return rint(x); }, AsD());
+ }
+
+ // Round of each component and return an integer vector.
+ VecTemplate<int> IRound() const {
+ using std::lrint;
+ return Generate<VecTemplate<int>>([](const T& x) { return lrint(x); },
+ AsD());
+ }
+
+ // True if any of the components is not a number.
+ bool IsNaN() const {
+ bool r = false;
+ const T* ap = AsD().Data();
+ for (int i = 0; i < SIZE; ++i)
+ r = r || isnan(ap[i]);
+ return r;
+ }
+
+ // A Vector populated with all NaN values.
+ static D NaN() {
+ return Generate([] { return std::numeric_limits<T>::quiet_NaN(); });
+ }
+
+ friend std::ostream& operator<<(std::ostream& out, const D& v) {
+ out << "[";
+ const char *sep = "";
+ for (int i = 0; i < SIZE; ++i) {
+ out << sep;
+ Print(out, v[i]);
+ sep = ", ";
+ }
+ return out << "]";
+ }
+
+ // These are only public for technical reasons (see cl/121145822).
+ template <typename K>
+ D MulScalarInternal(const K& k) const {
+ return Generate([k](const T& x) { return k * x; }, AsD());
+ }
+ template <typename K>
+ D DivScalarInternal(const K& k) const {
+ return Generate([k](const T& x) { return k / x; }, AsD());
+ }
+
+ private:
+ const D& AsD() const { return static_cast<const D&>(*this); }
+ D& AsD() { return static_cast<D&>(*this); }
+
+ // ostream << uint8 prints the ASCII character, which is not useful.
+ // Cast to int so that numbers will be printed instead.
+ template <typename U>
+ static void Print(std::ostream& out, const U& v) { out << v; }
+ static void Print(std::ostream& out, uint8 v) { out << static_cast<int>(v); }
+
+ // Ignores its arguments so that side-effects of variadic unpacking can occur.
+ static void Ignore(std::initializer_list<bool>) {}
+
+ template <std::size_t... Is>
+ static T Dot(T sum, const T* a, const T* b, absl::index_sequence<Is...>) {
+ Ignore({(sum += a[Is] * b[Is], true)...});
+ return sum;
+ }
+
+ template <std::size_t... Is>
+ static void PlusEq(T* a, const T* b, absl::index_sequence<Is...>) {
+ Ignore({(a[Is] += b[Is], true)...});
+ }
+
+ template <std::size_t... Is>
+ static void MinusEq(T* a, const T* b, absl::index_sequence<Is...>) {
+ Ignore({(a[Is] -= b[Is], true)...});
+ }
+
+ template <std::size_t... Is>
+ static void MulEq(T* a, T b, absl::index_sequence<Is...>) {
+ Ignore({(a[Is] *= b, true)...});
+ }
+
+ template <std::size_t... Is>
+ static void DivEq(T* a, T b, absl::index_sequence<Is...>) {
+ Ignore({(a[Is] /= b, true)...});
+ }
+};
+
+// These templates must be defined outside of BasicVector so that the
+// template specialization match algorithm must deduce 'a'. See the review
+// of cl/119944115.
+template <typename K,
+ template <typename> class VT2, typename T2, std::size_t N2>
+VT2<T2> operator*(const K& k, const BasicVector<VT2, T2, N2>& a) {
+ return a.MulScalarInternal(k);
+}
+template <typename K,
+ template <typename> class VT2, typename T2, std::size_t N2>
+VT2<T2> operator/(const K& k, const BasicVector<VT2, T2, N2>& a) {
+ return a.DivScalarInternal(k);
+}
+
+} // namespace internal_vector
+} // namespace math
+} // namespace util
+
+// ======================================================================
+template <typename T>
+class Vector2
+ : public util::math::internal_vector::BasicVector<Vector2, T, 2> {
+ private:
+ using Base = util::math::internal_vector::BasicVector<::Vector2, T, 2>;
+ using VType = T;
+
+ public:
+ typedef VType BaseType;
+ using FloatType = typename Base::FloatType;
+ using Base::SIZE;
+
+ Vector2() : c_() {}
+ Vector2(T x, T y) {
+ c_[0] = x;
+ c_[1] = y;
+ }
+ explicit Vector2(const Vector3<T> &b) : Vector2(b.x(), b.y()) {}
+ explicit Vector2(const Vector4<T> &b) : Vector2(b.x(), b.y()) {}
+
+ T* Data() { return c_; }
+ const T* Data() const { return c_; }
+
+ void x(T v) { c_[0] = v; }
+ void y(T v) { c_[1] = v; }
+ T x() const { return c_[0]; }
+ T y() const { return c_[1]; }
+
+ bool aequal(const Vector2 &vb, FloatType margin) const {
+ using std::fabs;
+ return (fabs(c_[0]-vb.c_[0]) < margin) && (fabs(c_[1]-vb.c_[1]) < margin);
+ }
+
+ void Set(T x, T y) { *this = Vector2(x, y); }
+
+ // Cross product. Be aware that if T is an integer type, the high bits
+ // of the result are silently discarded.
+ T CrossProd(const Vector2 &vb) const {
+ return c_[0] * vb.c_[1] - c_[1] * vb.c_[0];
+ }
+
+ // Returns the angle between "this" and v in radians. If either vector is
+ // zero-length, or nearly zero-length, the result will be zero, regardless of
+ // the other value.
+ FloatType Angle(const Vector2 &v) const {
+ using std::atan2;
+ return atan2(CrossProd(v), this->DotProd(v));
+ }
+
+ // return a vector orthogonal to the current one
+ // with the same norm and counterclockwise to it
+ Vector2 Ortho() const { return Vector2(-c_[1], c_[0]); }
+
+ // TODO(user): unify Fabs/Abs between all Vector classes.
+ Vector2 Fabs() const {
+ using std::fabs;
+ return Vector2(fabs(c_[0]), fabs(c_[1]));
+ }
+ Vector2 Abs() const {
+ static_assert(std::is_integral<VType>::value, "use Fabs for float_types");
+ static_assert(static_cast<VType>(-1) == -1, "type must be signed");
+ static_assert(sizeof(c_[0]) <= sizeof(int), "Abs truncates to int");
+ return Vector2(abs(c_[0]), abs(c_[1]));
+ }
+
+ private:
+ VType c_[SIZE];
+};
+
+template <typename T>
+class Vector3
+ : public util::math::internal_vector::BasicVector<Vector3, T, 3> {
+ private:
+ using Base = util::math::internal_vector::BasicVector<::Vector3, T, 3>;
+ using VType = T;
+
+ public:
+ typedef VType BaseType;
+ using FloatType = typename Base::FloatType;
+ using Base::SIZE;
+
+ Vector3() : c_() {}
+ Vector3(T x, T y, T z) {
+ c_[0] = x;
+ c_[1] = y;
+ c_[2] = z;
+ }
+ Vector3(const Vector2<T> &b, T z) : Vector3(b.x(), b.y(), z) {}
+ explicit Vector3(const Vector4<T> &b) : Vector3(b.x(), b.y(), b.z()) {}
+
+ T* Data() { return c_; }
+ const T* Data() const { return c_; }
+
+ void x(const T &v) { c_[0] = v; }
+ void y(const T &v) { c_[1] = v; }
+ void z(const T &v) { c_[2] = v; }
+ T x() const { return c_[0]; }
+ T y() const { return c_[1]; }
+ T z() const { return c_[2]; }
+
+ bool aequal(const Vector3 &vb, FloatType margin) const {
+ using std::abs;
+ return (abs(c_[0] - vb.c_[0]) < margin)
+ && (abs(c_[1] - vb.c_[1]) < margin)
+ && (abs(c_[2] - vb.c_[2]) < margin);
+ }
+
+ void Set(T x, T y, T z) { *this = Vector3(x, y, z); }
+
+ // Cross product. Be aware that if VType is an integer type, the high bits
+ // of the result are silently discarded.
+ Vector3 CrossProd(const Vector3& vb) const {
+ return Vector3(c_[1] * vb.c_[2] - c_[2] * vb.c_[1],
+ c_[2] * vb.c_[0] - c_[0] * vb.c_[2],
+ c_[0] * vb.c_[1] - c_[1] * vb.c_[0]);
+ }
+
+ // Returns a unit vector orthogonal to this one.
+ Vector3 Ortho() const {
+ int k = LargestAbsComponent() - 1;
+ if (k < 0) k = 2;
+ Vector3 temp;
+ temp[k] = T(1);
+ return CrossProd(temp).Normalize();
+ }
+
+ // Returns the angle between two vectors in radians. If either vector is
+ // zero-length, or nearly zero-length, the result will be zero, regardless of
+ // the other value.
+ FloatType Angle(const Vector3 &va) const {
+ using std::atan2;
+ return atan2(CrossProd(va).Norm(), this->DotProd(va));
+ }
+
+ Vector3 Fabs() const {
+ return Abs();
+ }
+
+ Vector3 Abs() const {
+ static_assert(
+ !std::is_integral<VType>::value || static_cast<VType>(-1) == -1,
+ "type must be signed");
+ using std::abs;
+ return Vector3(abs(c_[0]), abs(c_[1]), abs(c_[2]));
+ }
+
+ // return the index of the largest component (fabs)
+ int LargestAbsComponent() const {
+ Vector3 temp = Abs();
+ return temp[0] > temp[1] ?
+ temp[0] > temp[2] ? 0 : 2 :
+ temp[1] > temp[2] ? 1 : 2;
+ }
+
+ // return the index of the smallest, median ,largest component of the vector
+ Vector3<int> ComponentOrder() const {
+ using std::swap;
+ Vector3<int> temp(0, 1, 2);
+ if (c_[temp[0]] > c_[temp[1]]) swap(temp[0], temp[1]);
+ if (c_[temp[1]] > c_[temp[2]]) swap(temp[1], temp[2]);
+ if (c_[temp[0]] > c_[temp[1]]) swap(temp[0], temp[1]);
+ return temp;
+ }
+
+ private:
+ VType c_[SIZE];
+};
+
+template <typename T>
+class Vector4
+ : public util::math::internal_vector::BasicVector<Vector4, T, 4> {
+ private:
+ using Base = util::math::internal_vector::BasicVector<::Vector4, T, 4>;
+ using VType = T;
+
+ public:
+ typedef VType BaseType;
+ using FloatType = typename Base::FloatType;
+ using Base::SIZE;
+
+ Vector4() : c_() {}
+ Vector4(T x, T y, T z, T w) {
+ c_[0] = x;
+ c_[1] = y;
+ c_[2] = z;
+ c_[3] = w;
+ }
+
+ Vector4(const Vector2<T> &b, T z, T w)
+ : Vector4(b.x(), b.y(), z, w) {}
+ Vector4(const Vector2<T> &a, const Vector2<T> &b)
+ : Vector4(a.x(), a.y(), b.x(), b.y()) {}
+ Vector4(const Vector3<T> &b, T w)
+ : Vector4(b.x(), b.y(), b.z(), w) {}
+
+ T* Data() { return c_; }
+ const T* Data() const { return c_; }
+
+ bool aequal(const Vector4 &vb, FloatType margin) const {
+ using std::fabs;
+ return (fabs(c_[0] - vb.c_[0]) < margin)
+ && (fabs(c_[1] - vb.c_[1]) < margin)
+ && (fabs(c_[2] - vb.c_[2]) < margin)
+ && (fabs(c_[3] - vb.c_[3]) < margin);
+ }
+
+ void x(const T &v) { c_[0] = v; }
+ void y(const T &v) { c_[1] = v; }
+ void z(const T &v) { c_[2] = v; }
+ void w(const T &v) { c_[3] = v; }
+ T x() const { return c_[0]; }
+ T y() const { return c_[1]; }
+ T z() const { return c_[2]; }
+ T w() const { return c_[3]; }
+
+ void Set(T x, T y, T z, T w) { *this = Vector4(x, y, z, w); }
+
+ Vector4 Fabs() const {
+ using std::fabs;
+ return Vector4(fabs(c_[0]), fabs(c_[1]), fabs(c_[2]), fabs(c_[3]));
+ }
+
+ Vector4 Abs() const {
+ static_assert(std::is_integral<VType>::value, "use Fabs for float types");
+ static_assert(static_cast<VType>(-1) == -1, "type must be signed");
+ static_assert(sizeof(c_[0]) <= sizeof(int), "Abs truncates to int");
+ return Vector4(abs(c_[0]), abs(c_[1]), abs(c_[2]), abs(c_[3]));
+ }
+
+ private:
+ VType c_[SIZE];
+};
+
+typedef Vector2<uint8> Vector2_b;
+typedef Vector2<int16> Vector2_s;
+typedef Vector2<int> Vector2_i;
+typedef Vector2<float> Vector2_f;
+typedef Vector2<double> Vector2_d;
+
+typedef Vector3<uint8> Vector3_b;
+typedef Vector3<int16> Vector3_s;
+typedef Vector3<int> Vector3_i;
+typedef Vector3<float> Vector3_f;
+typedef Vector3<double> Vector3_d;
+
+typedef Vector4<uint8> Vector4_b;
+typedef Vector4<int16> Vector4_s;
+typedef Vector4<int> Vector4_i;
+typedef Vector4<float> Vector4_f;
+typedef Vector4<double> Vector4_d;
+
+
+#endif // S2_UTIL_MATH_VECTOR_H_
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef S2_UTIL_MATH_VECTOR3_HASH_H_
+#define S2_UTIL_MATH_VECTOR3_HASH_H_
+
+#include <cstddef>
+#include <functional>
+#include <type_traits>
+
+#include "s2/util/hash/mix.h"
+#include "s2/util/math/vector.h"
+
+template <class T>
+struct GoodFastHash;
+
+template <class VType>
+struct GoodFastHash<Vector2<VType>> {
+ std::size_t operator()(const Vector2<VType>& v) const {
+ static_assert(std::is_pod<VType>::value, "POD expected");
+ // std::hash collapses +/-0.
+ std::hash<VType> h;
+ HashMix mix(h(v.x()));
+ mix.Mix(h(v.y()));
+ return mix.get();
+ }
+};
+
+template <class VType>
+struct GoodFastHash<Vector3<VType>> {
+ std::size_t operator()(const Vector3<VType>& v) const {
+ static_assert(std::is_pod<VType>::value, "POD expected");
+ // std::hash collapses +/-0.
+ std::hash<VType> h;
+ HashMix mix(h(v.x()));
+ mix.Mix(h(v.y()));
+ mix.Mix(h(v.z()));
+ return mix.get();
+ }
+};
+
+#endif // S2_UTIL_MATH_VECTOR3_HASH_H_
--- /dev/null
+// Copyright 2003 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+
+// Define common length units. New length units can be added as needed
+// provided there is a direct, multiplicative scaling to meters.
+//
+// When adding new units, you should add an appropriate GetMyUnit accessor
+// template and (optionally) define a shorthand typedef specialization for
+// float. Also update the physical units unittest to check the conversion
+// ratio.
+
+#ifndef S2_UTIL_UNITS_LENGTH_UNITS_H_
+#define S2_UTIL_UNITS_LENGTH_UNITS_H_
+
+#include "s2/util/units/physical-units.h"
+
+namespace util {
+
+namespace units {
+
+struct LengthBase {
+ // the base unit type is meters
+ static const char* const output_suffix;
+
+ template <typename ValueType, class Unit>
+ static ValueType GetInBaseUnit(
+ const PhysicalUnit<ValueType, LengthBase, Unit> u) {
+ return GetMeters(u);
+ }
+};
+
+template <typename Float>
+class Length {
+ typedef UnitConversion<1, 1> MetersConversion;
+ typedef UnitConversion<1, 1000> KilometersConversion;
+ typedef UnitConversion<10000, 254> InchesConversion;
+ typedef UnitConversion<10000, 3048> FeetConversion;
+ typedef UnitConversion<1000, 1609344> MilesConversion;
+ typedef UnitConversion<1000, 1> MillimetersConversion;
+ typedef UnitConversion<1, 1852> NauticalMilesConversion;
+ typedef UnitConversion<10000, 9144> YardsConversion;
+
+ public:
+ typedef PhysicalUnit<Float, LengthBase, MetersConversion> Meters;
+ typedef PhysicalUnit<Float, LengthBase, KilometersConversion> Kilometers;
+ typedef PhysicalUnit<Float, LengthBase, InchesConversion> Inches;
+ typedef PhysicalUnit<Float, LengthBase, FeetConversion> Feet;
+ typedef PhysicalUnit<Float, LengthBase, MilesConversion> Miles;
+ typedef PhysicalUnit<Float, LengthBase, MillimetersConversion> Millimeters;
+ typedef PhysicalUnit<Float, LengthBase, NauticalMilesConversion>
+ NauticalMiles;
+ typedef PhysicalUnit<Float, LengthBase, YardsConversion> Yards;
+};
+
+// Define some shorthand, standard typenames. The standard length
+// type is chosen to be float. If you need greater precision for
+// a specific application, either use the fully qualified typename
+// Length<double>::* or declare local typedefs. This would be an
+// ideal place for a template typedef, if only C++ supported them.
+// Note that units with different floating-point types do not
+// support implicit conversion (use the precision_cast<...> method).
+typedef Length<float>::Meters Meters;
+typedef Length<float>::Kilometers Kilometers;
+typedef Length<float>::Inches Inches;
+typedef Length<float>::Feet Feet;
+typedef Length<float>::Miles Miles;
+typedef Length<float>::Millimeters Millimeters;
+typedef Length<float>::NauticalMiles NauticalMiles;
+typedef Length<float>::Yards Yards;
+
+// Explicit unit accessors. In general these are safer than using
+// the value() accessor, particularly in cases where the unit type
+// may not be immediately obvious (such as function returns).
+
+template <typename ValueType, class Unit>
+inline ValueType GetMeters(const PhysicalUnit<ValueType, LengthBase, Unit> u) {
+ return typename Length<ValueType>::Meters(u).value();
+}
+
+template <typename ValueType, class Unit>
+inline ValueType GetKilometers(
+ const PhysicalUnit<ValueType, LengthBase, Unit> u) {
+ return typename Length<ValueType>::Kilometers(u).value();
+}
+
+template <typename ValueType, class Unit>
+inline ValueType GetInches(const PhysicalUnit<ValueType, LengthBase, Unit> u) {
+ return typename Length<ValueType>::Inches(u).value();
+}
+
+template <typename ValueType, class Unit>
+inline ValueType GetFeet(const PhysicalUnit<ValueType, LengthBase, Unit> u) {
+ return typename Length<ValueType>::Feet(u).value();
+}
+
+template <typename ValueType, class Unit>
+inline ValueType GetMiles(const PhysicalUnit<ValueType, LengthBase, Unit> u) {
+ return typename Length<ValueType>::Miles(u).value();
+}
+
+template <typename ValueType, class Unit>
+inline ValueType GetMillimeters(
+ const PhysicalUnit<ValueType, LengthBase, Unit> u) {
+ return typename Length<ValueType>::Millimeters(u).value();
+}
+
+template <typename ValueType, class Unit>
+inline ValueType GetNauticalMiles(
+ const PhysicalUnit<ValueType, LengthBase, Unit> u) {
+ return typename Length<ValueType>::NauticalMiles(u).value();
+}
+
+template <typename ValueType, class Unit>
+inline ValueType GetYards(const PhysicalUnit<ValueType, LengthBase, Unit> u) {
+ return typename Length<ValueType>::Yards(u).value();
+}
+
+} // end namespace units
+
+} // end namespace util
+
+#endif // S2_UTIL_UNITS_LENGTH_UNITS_H_
--- /dev/null
+// Copyright 2003 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+
+// Basic physical unit classes. These classes are not as fancy as
+// some general purpose physical unit libraries, but provide a
+// simple and efficient interface for unit tracking and conversion.
+// In particular, compound units cannot be automatically constructed
+// from or decomposed into simpler units (e.g. velocity = distance /
+// time), but can be defined explicitly as opaque types.
+//
+// These classes define overloaded operators and non-explicit single
+// argument ctors, which breaks the style guidelines, but an exception
+// has been allowed in this case.
+// - Douglas Greiman <dgreiman@google.com>
+//
+// Specific types of physical units are defined in other headers
+// (e.g. angle-units.h). Each unit type can be specialized to either
+// float or double. Non-floating-point types are disallowed, since
+// the implicit conversion logic generally fails for integer division.
+// Attempting to declare integer-based units results in a fairly
+// informative compiler error.
+//
+// All units share common functionality, as demonstrated in this
+// example:
+//
+// #include "angle-units.h"
+//
+// class Nomad {
+// Radians latitude_;
+// ...
+// // With -O2 optimization, the use of Radians in this method is
+// // completely equivalent to using float, but prevents unitless
+// // angles from being passed.
+// void SetLatitude(Radians angle) {
+// S2_CHECK(angle.abs() < Degrees(90.0));
+// latitude_ = angle;
+// latitude_radius_ = EARTH_RADIUS * cos(angle.value());
+// }
+//
+// // This method contains an implicit unit conversion from degrees
+// // to radians. In practice it would make more sense to use
+// // Radians as the argument type to avoid this extra work if
+// // possible (see the two calls of this method below).
+// void MoveNorth(Degrees angle) {
+// SetLatitude(latitude_ + angle);
+// }
+// };
+//
+// void do_tests(float degrees_to_move, float radians_to_move) {
+// Nomad joe;
+//
+// // The use of Degrees(30.0) to set the latitude in radians requires
+// // no runtime conversion.
+// joe.SetLatitude(Degrees(30.0));
+//
+// // The Degrees(...) parameter will be converted to radians at
+// // runtime prior to addition in Nomad::MoveNorth().
+// joe.MoveNorth(Degrees(degrees_to_move));
+//
+// // This is ok, but due to the poor choice of units for the MoveNorth
+// // method's argument, incurs two pointless multiply operations to
+// // convert from radians to degrees and back to radians.
+// joe.MoveNorth(Radians(radians_to_move));
+//
+// // Implicit conversions from unitless values generate errors at
+// // compile time.
+// // joe.MoveNorth(degrees_to_move); // compile ERROR!
+// }
+//
+
+#ifndef S2_UTIL_UNITS_PHYSICAL_UNITS_H_
+#define S2_UTIL_UNITS_PHYSICAL_UNITS_H_
+
+#include <cmath>
+#include <iosfwd>
+#include <iostream>
+#include <string>
+#include <type_traits>
+
+#include "s2/base/integral_types.h"
+#include "s2/third_party/absl/base/macros.h"
+
+namespace util {
+
+namespace units {
+
+// Static conversion scale and offset to convert from a standard base
+// unit to a specific unit. The scale and offset is specified as a
+// rational number to allow static construction at compile time.
+template <int ScaleNumerator, int ScaleDenominator,
+ int OffsetNumerator = 0, int OffsetDenominator = 1>
+struct UnitConversion {
+ static const int SCALE_NUMERATOR = ScaleNumerator;
+ static const int SCALE_DENOMINATOR = ScaleDenominator;
+ static const int OFFSET_NUMERATOR = OffsetNumerator;
+ static const int OFFSET_DENOMINATOR = OffsetDenominator;
+};
+
+template <class FromUnit, class ToUnit, typename Float>
+struct UnitConverter {
+ // Linear unit conversion: A' = A * s + t, where
+ // - s is a static scale factor and
+ // - t is a static offset
+ // composed from two UnitConversion structs.
+ // Cast one multiplicand to 64 bit to ensure that the integer expression
+ // is computed in 64 bit. Otherwise Feet(Miles(x)) will overflow.
+ constexpr static inline Float Convert(Float value) {
+ // scaling and offset
+ return static_cast<Float>(
+ (static_cast<double>(value *
+ (static_cast<double>(static_cast<uint64>(ToUnit::SCALE_NUMERATOR) *
+ FromUnit::SCALE_DENOMINATOR) /
+ static_cast<double>(static_cast<uint64>(ToUnit::SCALE_DENOMINATOR) *
+ FromUnit::SCALE_NUMERATOR)))) -
+ (static_cast<double>(static_cast<uint64>(ToUnit::SCALE_NUMERATOR) *
+ FromUnit::SCALE_DENOMINATOR *
+ FromUnit::OFFSET_NUMERATOR) /
+ static_cast<double>(static_cast<uint64>(ToUnit::SCALE_DENOMINATOR) *
+ FromUnit::SCALE_NUMERATOR *
+ FromUnit::OFFSET_DENOMINATOR)) +
+ (static_cast<double>(ToUnit::OFFSET_NUMERATOR) /
+ static_cast<double>(ToUnit::OFFSET_DENOMINATOR)));
+ }
+};
+
+// Some unit operations are only defined for base units that have linear
+// transformations, as in T(a+b) = T(a) + T(b). Temperatures units are
+// an example of units that do not have linear transformations. By
+// default unit transformations are assumed to be linear; see
+// temperature-units.h for an example of how to override this default.
+template <class Base>
+struct is_linear_unit_transformation : std::true_type { };
+
+// Template class holding a single value with an associated physical
+// unit. The unit and conversion parameters are statically defined
+// and optimized at compile time. With optimization (-O2), use of a
+// single physical unit type is as efficient as using a native
+// floating point type. Conversions between units are optimized to
+// (typically) a single multiplication operation. Unit conversions
+// for constants are done at compile time and incur no runtime
+// overhead (again at -O2).
+//
+// Template parameters:
+// Base is the base unit class, such as Angle or Length. If operator<<
+// is used, it must have:
+// - a public static field "output_suffix" and
+// - a public static method Float Base::GetInBaseUnit(PhysicalUnit). An
+// example can be found in length-units.h
+// LengthBase::GetInBaseUnit.
+// Unit is the UnitConversion class that defines a specific unit
+// (such as degrees) in terms of a reference unit (such as radians).
+template <class Float, class Base, class Unit>
+class PhysicalUnit {
+ public:
+ typedef PhysicalUnit<Float, Base, Unit> Type;
+ typedef Float FloatType;
+
+ // Use 'explicit' to prevent unintentional construction from untyped (or
+ // mistyped) values. Note that this also prevents arguably reasonable
+ // constructs such as Unit unit = 10.0; use either Unit unit(10.0) or
+ // Unit unit = Unit(10.0) instead.
+ PhysicalUnit(): value_(static_cast<Float>(0.0)) {}
+ constexpr explicit PhysicalUnit(Float value): value_(value) {}
+
+ // Conversion from other units of the same Base type.
+ //
+ // Policy decision: not using 'explicit' allows much more natural
+ // conversions between units of a given base type. This can result in
+ // unintended implicit type conversions, but these incur very little
+ // overhead (inlined multiply at -O2) and should be inconsequential in
+ // most circumstances. Casts between different base types (including
+ // different underlying value types) require explicit handling. The ClangTidy
+ // warnings regarding this are therefore suppressed with NOLINT below.
+ template <class Unit2>
+ constexpr PhysicalUnit(PhysicalUnit<Float, Base, Unit2> other) // NOLINT
+ : value_(UnitConverter<Unit2, Unit, Float>::Convert(other.value())) {}
+
+ // Copy operation from other units of the same Base type.
+ template <class Unit2>
+ Type operator = (PhysicalUnit<Float, Base, Unit2> other) {
+ value_ = UnitConverter<Unit2, Unit, Float>::Convert(other.value());
+ return *this;
+ }
+
+ // Value accessor. Consider using an explicitly typed accessor whenever
+ // the unit type is not immediately obvious (such as function return
+ // values). For example:
+ // float x = myclass.GetAngle().value(); // what unit is x?
+ // float x = GetDegrees(myclass.GetAngle()); // much clearer.
+ // float x = Degrees(myclass.GetAngle()).value(); // ok too.
+ // Degrees degrees = myclass.GetAngle(); // using a temporary is
+ // float x = degrees.value(); // also good.
+ constexpr Float value() const { return value_; }
+
+ // Trivial arithematic operator wrapping.
+ Type operator - () const {
+ return Type(-value_);
+ }
+ Type operator * (const Float scale) const {
+ return Type(value_ * scale);
+ }
+ Type operator + (const Type other) const {
+ static_assert(is_linear_unit_transformation<Base>::value,
+ "operation not defined");
+ return Type(value_ + other.value());
+ }
+ Type operator - (const Type other) const {
+ static_assert(is_linear_unit_transformation<Base>::value,
+ "operation not defined");
+ return Type(value_ - other.value());
+ }
+ Float operator / (const Type other) const {
+ return value_ / other.value();
+ }
+ Type operator *= (const Float scale) {
+ value_ *= scale;
+ return *this;
+ }
+ Type operator += (const Type other) {
+ static_assert(is_linear_unit_transformation<Base>::value,
+ "operation not defined");
+ value_ += other.value();
+ return *this;
+ }
+ Type operator -= (const Type other) {
+ static_assert(is_linear_unit_transformation<Base>::value,
+ "operation not defined");
+ value_ -= other.value();
+ return *this;
+ }
+
+ // Simple comparisons. Overloaded equality is intentionally omitted;
+ // use equals() instead.
+ bool operator < (const Type other) const {
+ return value_ < other.value();
+ }
+ bool operator > (const Type other) const {
+ return value_ > other.value();
+ }
+ bool operator <= (const Type other) const {
+ return value_ <= other.value();
+ }
+ bool operator >= (const Type other) const {
+ return value_ >= other.value();
+ }
+
+ // Test equality to within some epsilon. Always false for
+ // epsilon < 0.
+ bool equals(const Type other,
+ const Type epsilon) const {
+ Float delta = value_ - other.value_;
+ if (delta < static_cast<Float>(0.0)) {
+ return -delta <= epsilon.value_;
+ }
+ return delta <= epsilon.value_;
+ }
+
+ // A little more sugar.
+ Type abs() const {
+ return (value_ < static_cast<Float>(0.0)) ? -(*this) : *this;
+ }
+
+ // Explicit precision casting within a base unit class.
+ template <typename OtherFloat>
+ PhysicalUnit<OtherFloat, Base, Unit> precision_cast() const {
+ typedef PhysicalUnit<OtherFloat, Base, Unit> CastType;
+ return CastType(static_cast<OtherFloat>(value_));
+ }
+
+ private:
+ Float value_;
+ // Enforce Float to be a floating point type, since unit conversions will
+ // generally fail with integers.
+ static_assert(std::is_floating_point<Float>::value,
+ "Only_use_floating_point_types");
+};
+
+// Allow 2*u (in addition to u*2).
+template <typename Scale, typename Float, class Base, class Unit>
+inline PhysicalUnit<Float, Base, Unit> operator * (
+ const Scale scale,
+ const PhysicalUnit<Float, Base, Unit> value) {
+ return value * static_cast<Float>(scale);
+}
+
+// we'll print the current value and the value converted to the natural
+// base type
+template <typename Float, typename Base, typename Unit>
+std::ostream& operator<<(std::ostream& os,
+ PhysicalUnit<Float, Base, Unit> value) {
+ return os << value.value()
+ << " (" << Base::GetInBaseUnit(value)
+ << Base::output_suffix << ")";
+}
+
+} // end namespace units
+
+} // end namespace util
+
+#endif // S2_UTIL_UNITS_PHYSICAL_UNITS_H_
--- /dev/null
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_VALUE_LEXICON_H_
+#define S2_VALUE_LEXICON_H_
+
+#include <functional>
+#include <limits>
+#include <vector>
+
+#include "s2/base/integral_types.h"
+#include "s2/util/gtl/dense_hash_set.h"
+
+// ValueLexicon is a class that maps distinct values to sequentially numbered
+// integer identifiers. It automatically eliminates duplicates and uses a
+// compact representation. See also SequenceLexicon.
+//
+// Each distinct value is mapped to a 32-bit integer. The space used for each
+// value is approximately 7 bytes plus the space needed for the value itself.
+// For example, int64 values would need approximately 15 bytes each. Note
+// also that values are referred to using 32-bit ids rather than 64-bit
+// pointers.
+//
+// This class has the same thread-safety properties as "string": const methods
+// are thread safe, and non-const methods are not thread safe.
+//
+// Example usage:
+//
+// ValueLexicon<string> lexicon;
+// uint32 cat_id = lexicon.Add("cat");
+// EXPECT_EQ(cat_id, lexicon.Add("cat"));
+// EXPECT_EQ("cat", lexicon.value(cat_id));
+//
+template <class T,
+ class Hasher = std::hash<T>,
+ class KeyEqual = std::equal_to<T>>
+class ValueLexicon {
+ public:
+ explicit ValueLexicon(const Hasher& hasher = Hasher(),
+ const KeyEqual& key_equal = KeyEqual());
+
+ // ValueLexicon is movable and copyable.
+ ValueLexicon(const ValueLexicon&);
+ ValueLexicon& operator=(const ValueLexicon&);
+ ValueLexicon(ValueLexicon&&);
+ ValueLexicon& operator=(ValueLexicon&&);
+
+ // Clears all data from the lexicon.
+ void Clear();
+
+ // Add the given value to the lexicon if it is not already present, and
+ // return its integer id. Ids are assigned sequentially starting from zero.
+ uint32 Add(const T& value);
+
+ // Return the number of values in the lexicon.
+ uint32 size() const;
+
+ // Return the value with the given id.
+ const T& value(uint32 id) const;
+
+ private:
+ friend class IdKeyEqual;
+ // Choose kEmptyKey to be the last key that will ever be generated.
+ static const uint32 kEmptyKey = std::numeric_limits<uint32>::max();
+
+ class IdHasher {
+ public:
+ IdHasher(const Hasher& hasher, const ValueLexicon* lexicon);
+ const Hasher& hasher() const;
+ size_t operator()(uint32 id) const;
+ private:
+ Hasher hasher_;
+ const ValueLexicon* lexicon_;
+ };
+
+ class IdKeyEqual {
+ public:
+ IdKeyEqual(const KeyEqual& key_equal, const ValueLexicon* lexicon);
+ bool operator()(uint32 id1, uint32 id2) const;
+ private:
+ KeyEqual key_equal_;
+ const ValueLexicon* lexicon_;
+ };
+
+ using IdSet = gtl::dense_hash_set<uint32, IdHasher, IdKeyEqual>;
+
+ KeyEqual key_equal_;
+ std::vector<T> values_;
+ IdSet id_set_;
+};
+
+
+////////////////// Implementation details follow ////////////////////
+
+
+template <class T, class Hasher, class KeyEqual>
+const uint32 ValueLexicon<T, Hasher, KeyEqual>::kEmptyKey;
+
+template <class T, class Hasher, class KeyEqual>
+ValueLexicon<T, Hasher, KeyEqual>::IdHasher::IdHasher(
+ const Hasher& hasher, const ValueLexicon* lexicon)
+ : hasher_(hasher), lexicon_(lexicon) {
+}
+
+template <class T, class Hasher, class KeyEqual>
+const Hasher& ValueLexicon<T, Hasher, KeyEqual>::IdHasher::hasher() const {
+ return hasher_;
+}
+
+template <class T, class Hasher, class KeyEqual>
+inline size_t ValueLexicon<T, Hasher, KeyEqual>::IdHasher::operator()(
+ uint32 id) const {
+ return hasher_(lexicon_->value(id));
+}
+
+template <class T, class Hasher, class KeyEqual>
+ValueLexicon<T, Hasher, KeyEqual>::IdKeyEqual::IdKeyEqual(
+ const KeyEqual& key_equal, const ValueLexicon* lexicon)
+ : key_equal_(key_equal), lexicon_(lexicon) {
+}
+
+template <class T, class Hasher, class KeyEqual>
+inline bool ValueLexicon<T, Hasher, KeyEqual>::IdKeyEqual::operator()(
+ uint32 id1, uint32 id2) const {
+ if (id1 == id2) return true;
+ if (id1 == lexicon_->kEmptyKey || id2 == lexicon_->kEmptyKey) {
+ return false;
+ }
+ return key_equal_(lexicon_->value(id1), lexicon_->value(id2));
+}
+
+template <class T, class Hasher, class KeyEqual>
+ValueLexicon<T, Hasher, KeyEqual>::ValueLexicon(const Hasher& hasher,
+ const KeyEqual& key_equal)
+ : key_equal_(key_equal),
+ id_set_(0, IdHasher(hasher, this),
+ IdKeyEqual(key_equal, this)) {
+ id_set_.set_empty_key(kEmptyKey);
+}
+
+template <class T, class Hasher, class KeyEqual>
+ValueLexicon<T, Hasher, KeyEqual>::ValueLexicon(const ValueLexicon& x)
+ : key_equal_(x.key_equal_), values_(x.values_),
+ // Unfortunately we can't copy "id_set_" because we need to change the
+ // "this" pointers associated with hasher() and key_equal().
+ id_set_(x.id_set_.begin(), x.id_set_.end(), kEmptyKey, 0,
+ IdHasher(x.id_set_.hash_funct().hasher(), this),
+ IdKeyEqual(x.key_equal_, this)) {
+}
+
+template <class T, class Hasher, class KeyEqual>
+ValueLexicon<T, Hasher, KeyEqual>::ValueLexicon(ValueLexicon&& x)
+ : key_equal_(std::move(x.key_equal_)), values_(std::move(x.values_)),
+ // Unfortunately we can't move "id_set_" because we need to change the
+ // "this" pointers associated with hasher() and key_equal().
+ id_set_(x.id_set_.begin(), x.id_set_.end(), kEmptyKey, 0,
+ IdHasher(x.id_set_.hash_funct().hasher(), this),
+ IdKeyEqual(x.key_equal_, this)) {
+}
+
+template <class T, class Hasher, class KeyEqual>
+ValueLexicon<T, Hasher, KeyEqual>&
+ValueLexicon<T, Hasher, KeyEqual>::operator=(const ValueLexicon& x) {
+ // Note that self-assignment is handled correctly by this code.
+ key_equal_ = x.key_equal_;
+ values_ = x.values_;
+ // Unfortunately we can't copy-assign "id_set_" because we need to change
+ // the "this" pointers associated with hasher() and key_equal().
+ id_set_ = IdSet(x.id_set_.begin(), x.id_set_.end(), kEmptyKey, 0,
+ IdHasher(x.id_set_.hash_funct().hasher(), this),
+ IdKeyEqual(x.key_equal_, this));
+ return *this;
+}
+
+template <class T, class Hasher, class KeyEqual>
+ValueLexicon<T, Hasher, KeyEqual>&
+ValueLexicon<T, Hasher, KeyEqual>::operator=(ValueLexicon&& x) {
+ // Note that move self-assignment has undefined behavior.
+ key_equal_ = std::move(x.key_equal_);
+ values_ = std::move(x.values_);
+ // Unfortunately we can't move-assign "id_set_" because we need to change
+ // the "this" pointers associated with hasher() and key_equal().
+ id_set_ = IdSet(x.id_set_.begin(), x.id_set_.end(), kEmptyKey, 0,
+ IdHasher(x.id_set_.hash_funct().hasher(), this),
+ IdKeyEqual(x.key_equal_, this));
+ return *this;
+}
+
+template <class T, class Hasher, class KeyEqual>
+void ValueLexicon<T, Hasher, KeyEqual>::Clear() {
+ values_.clear();
+ id_set_.clear();
+}
+
+template <class T, class Hasher, class KeyEqual>
+uint32 ValueLexicon<T, Hasher, KeyEqual>::Add(const T& value) {
+ if (!values_.empty() && key_equal_(value, values_.back())) {
+ return values_.size() - 1;
+ }
+ values_.push_back(value);
+ auto result = id_set_.insert(values_.size() - 1);
+ if (result.second) {
+ return values_.size() - 1;
+ } else {
+ values_.pop_back();
+ return *result.first;
+ }
+}
+
+template <class T, class Hasher, class KeyEqual>
+inline uint32 ValueLexicon<T, Hasher, KeyEqual>::size() const {
+ return values_.size();
+}
+
+template <class T, class Hasher, class KeyEqual>
+inline const T& ValueLexicon<T, Hasher, KeyEqual>::value(uint32 id) const {
+ return values_[id];
+}
+
+#endif // S2_VALUE_LEXICON_H_
--- /dev/null
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/s2-geography.R
+\name{as_s2_geography}
+\alias{as_s2_geography}
+\alias{as_s2_geography.default}
+\alias{as_s2_geography.s2_geography}
+\alias{as_s2_geography.s2_lnglat}
+\alias{as_s2_geography.s2_point}
+\alias{as_s2_geography.wk_wkb}
+\alias{as_s2_geography.WKB}
+\alias{as_s2_geography.blob}
+\alias{as_s2_geography.wk_wkt}
+\alias{as_s2_geography.character}
+\alias{as_s2_geography.logical}
+\alias{as_wkb.s2_geography}
+\alias{as_wkt.s2_geography}
+\title{Create an S2 Geography Vector}
+\usage{
+as_s2_geography(x, ...)
+
+\method{as_s2_geography}{default}(x, ...)
+
+\method{as_s2_geography}{s2_geography}(x, ...)
+
+\method{as_s2_geography}{s2_lnglat}(x, ...)
+
+\method{as_s2_geography}{s2_point}(x, ...)
+
+\method{as_s2_geography}{wk_wkb}(x, ..., oriented = FALSE, check = TRUE)
+
+\method{as_s2_geography}{WKB}(x, ..., oriented = FALSE, check = TRUE)
+
+\method{as_s2_geography}{blob}(x, ..., oriented = FALSE, check = TRUE)
+
+\method{as_s2_geography}{wk_wkt}(x, ..., oriented = FALSE, check = TRUE)
+
+\method{as_s2_geography}{character}(x, ..., oriented = FALSE, check = TRUE)
+
+\method{as_s2_geography}{logical}(x, ...)
+
+\method{as_wkb}{s2_geography}(x, ...)
+
+\method{as_wkt}{s2_geography}(x, ...)
+}
+\arguments{
+\item{x}{An object that can be converted to an s2_geography vector}
+
+\item{...}{Unused}
+
+\item{oriented}{TRUE if polygon ring directions are known to be correct
+(i.e., exterior rings are defined counter clockwise and interior
+rings are defined clockwise).}
+
+\item{check}{Use \code{check = FALSE} to skip error on invalid geometries}
+}
+\value{
+An object with class s2_geography
+}
+\description{
+Geography vectors are arrays of points, lines, polygons, and/or collections
+of these. Geography vectors assume coordinates are longitude and latitude
+on a perfect sphere.
+}
+\details{
+The coercion function \code{\link[=as_s2_geography]{as_s2_geography()}} is used to wrap the input
+of most functions in the s2 package so that you can use other objects with
+an unambiguious interpretation as a geography vector. Geography vectors
+have a minimal \link[vctrs:vctrs-package]{vctrs} implementation, so you can
+use these objects in tibble, dplyr, and other packages that use the vctrs
+framework.
+}
+\seealso{
+\code{\link[=s2_geog_from_wkb]{s2_geog_from_wkb()}}, \code{\link[=s2_geog_from_text]{s2_geog_from_text()}}, \code{\link[=s2_geog_point]{s2_geog_point()}},
+\code{\link[=s2_make_line]{s2_make_line()}}, \code{\link[=s2_make_polygon]{s2_make_polygon()}} for other ways to
+create geography vectors, and \code{\link[=s2_as_binary]{s2_as_binary()}} and \code{\link[=s2_as_text]{s2_as_text()}}
+for other ways to export them.
+}
--- /dev/null
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/s2-package.R
+\docType{package}
+\name{s2-package}
+\alias{s2}
+\alias{s2-package}
+\title{s2: Spherical Geometry Operators Using the S2 Geometry Library}
+\description{
+Provides R bindings for Google's s2 library for geometric calculations on
+ the sphere. High-performance constructors and exporters provide high compatibility
+ with existing spatial packages, transformers construct new geometries from existing
+ geometries, predicates provide a means to select geometries based on spatial
+ relationships, and accessors extract information about geometries.
+}
+\seealso{
+Useful links:
+\itemize{
+ \item \url{https://r-spatial.github.io/s2/}
+ \item \url{https://github.com/r-spatial/s2}
+ \item \url{https://s2geometry.io/}
+ \item Report bugs at \url{https://github.com/r-spatial/s2/issues}
+}
+
+}
+\author{
+\strong{Maintainer}: Edzer Pebesma \email{edzer.pebesma@uni-muenster.de} (\href{https://orcid.org/0000-0001-8049-7069}{ORCID})
+
+Authors:
+\itemize{
+ \item Dewey Dunnington \email{dewey@fishandwhistle.net} (\href{https://orcid.org/0000-0002-9415-4582}{ORCID})
+ \item Ege Rubak \email{rubak@math.aau.dk}
+}
+
+Other contributors:
+\itemize{
+ \item Jeroen Ooms \email{jeroen.ooms@stat.ucla.edu} (configure script) [contributor]
+ \item Google, Inc. (Original s2geometry.io source code) [copyright holder]
+}
+
+}
+\keyword{internal}
--- /dev/null
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/s2-transformers.R
+\name{s2_boundary}
+\alias{s2_boundary}
+\alias{s2_centroid}
+\alias{s2_closest_point}
+\alias{s2_minimum_clearance_line_between}
+\alias{s2_difference}
+\alias{s2_sym_difference}
+\alias{s2_intersection}
+\alias{s2_union}
+\alias{s2_snap_to_grid}
+\alias{s2_simplify}
+\alias{s2_rebuild}
+\alias{s2_buffer_cells}
+\alias{s2_centroid_agg}
+\alias{s2_union_agg}
+\title{S2 Geography Transformations}
+\usage{
+s2_boundary(x)
+
+s2_centroid(x)
+
+s2_closest_point(x, y)
+
+s2_minimum_clearance_line_between(x, y)
+
+s2_difference(x, y, options = s2_options())
+
+s2_sym_difference(x, y, options = s2_options())
+
+s2_intersection(x, y, options = s2_options())
+
+s2_union(x, y = NULL, options = s2_options())
+
+s2_snap_to_grid(x, grid_size)
+
+s2_simplify(x, tolerance, radius = s2_earth_radius_meters())
+
+s2_rebuild(x, options = s2_options())
+
+s2_buffer_cells(
+ x,
+ distance,
+ max_cells = 1000,
+ min_level = -1,
+ radius = s2_earth_radius_meters()
+)
+
+s2_centroid_agg(x, na.rm = FALSE)
+
+s2_union_agg(x, options = s2_options(), na.rm = FALSE)
+}
+\arguments{
+\item{x}{\link[=as_s2_geography]{geography vectors}. These inputs
+are passed to \code{\link[=as_s2_geography]{as_s2_geography()}}, so you can pass other objects
+(e.g., character vectors of well-known text) directly.}
+
+\item{y}{\link[=as_s2_geography]{geography vectors}. These inputs
+are passed to \code{\link[=as_s2_geography]{as_s2_geography()}}, so you can pass other objects
+(e.g., character vectors of well-known text) directly.}
+
+\item{options}{An \code{\link[=s2_options]{s2_options()}} object describing the polygon/polyline
+model to use and the snap level.}
+
+\item{grid_size}{The grid size to which coordinates should be snapped;
+will be rounded to the nearest power of 10.}
+
+\item{tolerance}{The minimum distance between vertexes to use when
+simplifying a geography.}
+
+\item{radius}{Radius of the earth. Defaults to the average radius of
+the earth in meters as defined by \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}}.}
+
+\item{distance}{The distance to buffer, in units of \code{radius}.}
+
+\item{max_cells}{The maximum number of cells to approximate a buffer.}
+
+\item{min_level}{The minimum cell level used to approximate a buffer
+(1 - 30). Setting this value too high will result in unnecessarily
+large geographies, but may help improve buffers along long, narrow
+regions.}
+
+\item{na.rm}{For aggregate calculations use \code{na.rm = TRUE}
+to drop missing values.}
+}
+\description{
+These functions operate on one or more geography vectors and
+return a geography vector.
+}
+\section{Model}{
+
+The geometry model indicates whether or not a geometry includes its boundaries.
+Boundaries of line geometries are its end points.
+OPEN geometries do not contain their boundary (\code{model = "open"}); CLOSED
+geometries (\code{model = "closed"}) contain their boundary; SEMI-OPEN geometries
+(\code{model = "semi-open"}) contain half of their boundaries, such that when two polygons
+do not overlap or two lines do not cross, no point exist that belong to
+more than one of the geometries. (This latter form, half-closed, is
+not present in the OpenGIS "simple feature access" (SFA) standard nor DE9-IM on
+which that is based). The default values for \code{\link[=s2_contains]{s2_contains()}} (open)
+and covers/covered_by (closed) correspond to the SFA standard specification
+of these operators.
+}
+
+\examples{
+# returns the boundary:
+# empty for point, endpoints of a linestring,
+# perimeter of a polygon
+s2_boundary("POINT (-64 45)")
+s2_boundary("LINESTRING (0 0, 10 0)")
+s2_boundary("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))")
+
+# returns the area-weighted centroid, element-wise
+s2_centroid("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))")
+s2_centroid("LINESTRING (0 0, 10 0)")
+
+# returns the unweighted centroid of the entire input
+s2_centroid_agg(c("POINT (0 0)", "POINT (10 0)"))
+
+# returns the closest point on x to y
+s2_closest_point(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POINT (0 90)" # north pole!
+)
+
+# returns the shortest possible line between x and y
+s2_minimum_clearance_line_between(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POINT (0 90)" # north pole!
+)
+
+# binary operations: difference, symmetric difference, intersection and union
+s2_difference(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))",
+ # 32 bit platforms may need to set snap rounding
+ s2_options(snap = s2_snap_level(30))
+)
+
+s2_sym_difference(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))",
+ # 32 bit platforms may need to set snap rounding
+ s2_options(snap = s2_snap_level(30))
+)
+
+s2_intersection(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))",
+ # 32 bit platforms may need to set snap rounding
+ s2_options(snap = s2_snap_level(30))
+)
+
+s2_union(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))",
+ # 32 bit platforms may need to set snap rounding
+ s2_options(snap = s2_snap_level(30))
+)
+
+# use s2_union_agg() to aggregate geographies in a vector
+s2_union_agg(
+ c(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))"
+ ),
+ # 32 bit platforms may need to set snap rounding
+ s2_options(snap = s2_snap_level(30))
+)
+
+# snap to grid rounds coordinates to a specified grid size
+s2_snap_to_grid("POINT (0.333333333333 0.666666666666)", 1e-2)
+
+}
+\seealso{
+BigQuery's geography function reference:
+\itemize{
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_boundary}{ST_BOUNDARY}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_centroid}{ST_CENTROID}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_closestpoint}{ST_CLOSESTPOINT}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_difference}{ST_DIFFERENCE}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_intersection}{ST_INTERSECTION}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_union}{ST_UNION}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_snaptogrid}{ST_SNAPTOGRID}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_simplify}{ST_SIMPLIFY}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_union_agg}{ST_UNION_AGG}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#s2_centroid_agg}{ST_CENTROID_AGG}
+}
+}
--- /dev/null
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/s2-bounds.R
+\name{s2_bounds_cap}
+\alias{s2_bounds_cap}
+\alias{s2_bounds_rect}
+\title{Compute feature-wise and aggregate bounds}
+\usage{
+s2_bounds_cap(x)
+
+s2_bounds_rect(x)
+}
+\arguments{
+\item{x}{\link[=as_s2_geography]{geography vectors}. These inputs
+are passed to \code{\link[=as_s2_geography]{as_s2_geography()}}, so you can pass other objects
+(e.g., character vectors of well-known text) directly.}
+}
+\value{
+Both functions return a \code{data.frame}:
+\itemize{
+\item \code{\link[=s2_bounds_rect]{s2_bounds_rect()}}: Columns \code{minlng}, \code{minlat}, \code{maxlng}, \code{maxlat} (degrees)
+\item \code{\link[=s2_bounds_cap]{s2_bounds_cap()}}: Columns \code{lng}, \code{lat}, \code{angle} (degrees)
+}
+}
+\description{
+\code{\link[=s2_bounds_rect]{s2_bounds_rect()}} returns a bounding latitude-longitude
+rectangle that contains the region; \code{\link[=s2_bounds_cap]{s2_bounds_cap()}} returns a bounding circle
+represented by a centre point (lat, lng) and an angle. The bound may not be tight
+for points, polylines and geometry collections. The rectangle returned may depend on
+the order of points or polylines. \code{lng_lo} values larger than \code{lng_hi} indicate
+regions that span the antimeridian, see the Fiji example.
+}
+\examples{
+s2_bounds_cap(s2_data_countries("Antarctica"))
+s2_bounds_cap(s2_data_countries("Netherlands"))
+s2_bounds_cap(s2_data_countries("Fiji"))
+
+s2_bounds_rect(s2_data_countries("Antarctica"))
+s2_bounds_rect(s2_data_countries("Netherlands"))
+s2_bounds_rect(s2_data_countries("Fiji"))
+
+}
--- /dev/null
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/s2-matrix.R
+\name{s2_closest_feature}
+\alias{s2_closest_feature}
+\alias{s2_farthest_feature}
+\alias{s2_distance_matrix}
+\alias{s2_max_distance_matrix}
+\alias{s2_contains_matrix}
+\alias{s2_within_matrix}
+\alias{s2_covers_matrix}
+\alias{s2_covered_by_matrix}
+\alias{s2_intersects_matrix}
+\alias{s2_disjoint_matrix}
+\alias{s2_equals_matrix}
+\alias{s2_touches_matrix}
+\alias{s2_dwithin_matrix}
+\alias{s2_may_intersect_matrix}
+\title{Matrix Functions}
+\usage{
+s2_closest_feature(x, y)
+
+s2_farthest_feature(x, y)
+
+s2_distance_matrix(x, y, radius = s2_earth_radius_meters())
+
+s2_max_distance_matrix(x, y, radius = s2_earth_radius_meters())
+
+s2_contains_matrix(x, y, options = s2_options(model = "open"))
+
+s2_within_matrix(x, y, options = s2_options(model = "open"))
+
+s2_covers_matrix(x, y, options = s2_options(model = "closed"))
+
+s2_covered_by_matrix(x, y, options = s2_options(model = "closed"))
+
+s2_intersects_matrix(x, y, options = s2_options())
+
+s2_disjoint_matrix(x, y, options = s2_options())
+
+s2_equals_matrix(x, y, options = s2_options())
+
+s2_touches_matrix(x, y, options = s2_options())
+
+s2_dwithin_matrix(x, y, distance, radius = s2_earth_radius_meters())
+
+s2_may_intersect_matrix(x, y, max_edges_per_cell = 50, max_feature_cells = 4)
+}
+\arguments{
+\item{x, y}{Geography vectors, coerced using \code{\link[=as_s2_geography]{as_s2_geography()}}.
+\code{x} is considered the source, where as \code{y} is considered the target.}
+
+\item{radius}{Radius of the earth. Defaults to the average radius of
+the earth in meters as defined by \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}}.}
+
+\item{options}{An \code{\link[=s2_options]{s2_options()}} object describing the polygon/polyline
+model to use and the snap level.}
+
+\item{distance}{A distance on the surface of the earth in the same units
+as \code{radius}.}
+
+\item{max_edges_per_cell}{For \code{\link[=s2_may_intersect_matrix]{s2_may_intersect_matrix()}},
+this values controls the nature of the index on \code{y}, with higher values
+leading to coarser index. Values should be between 10 and 50; the default
+of 50 is adequate for most use cases, but for specialized operations users
+may wish to use a lower value to increase performance.}
+
+\item{max_feature_cells}{For \code{\link[=s2_may_intersect_matrix]{s2_may_intersect_matrix()}}, this value
+controls the approximation of \code{x} used to identify potential intersections
+on \code{y}. The default value of 4 gives the best performance for most operations,
+but for specialized operations users may wish to use a higher value to increase
+performance.}
+}
+\value{
+A vector of length \code{x}.
+}
+\description{
+These functions are similar to accessors and predicates, but instead of
+recycling \code{x} and \code{y} to a common length and returning a vector of that
+length, these functions return a vector of length \code{x} with each element
+\code{i} containing information about how the entire vector \code{y} relates to
+the feature at \code{x[i]}.
+}
+\examples{
+city_names <- c("Vatican City", "San Marino", "Luxembourg")
+cities <- s2_data_cities(city_names)
+country_names <- s2_data_tbl_countries$name
+countries <- s2_data_countries()
+
+# closest feature returns y indices of the closest feature
+# for each feature in x
+country_names[s2_closest_feature(cities, countries)]
+
+# farthest feature returns y indices of the farthest feature
+# for each feature in x
+country_names[s2_farthest_feature(cities, countries)]
+
+# predicate matrices
+country_names[s2_intersects_matrix(cities, countries)[[1]]]
+
+# distance matrices
+s2_distance_matrix(cities, cities)
+s2_max_distance_matrix(cities, countries[1:4])
+
+}
+\seealso{
+See pairwise predicate functions (e.g., \code{\link[=s2_intersects]{s2_intersects()}}).
+}
--- /dev/null
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/s2-predicates.R
+\name{s2_contains}
+\alias{s2_contains}
+\alias{s2_within}
+\alias{s2_covered_by}
+\alias{s2_covers}
+\alias{s2_disjoint}
+\alias{s2_intersects}
+\alias{s2_equals}
+\alias{s2_intersects_box}
+\alias{s2_touches}
+\alias{s2_dwithin}
+\title{S2 Geography Predicates}
+\usage{
+s2_contains(x, y, options = s2_options(model = "open"))
+
+s2_within(x, y, options = s2_options(model = "open"))
+
+s2_covered_by(x, y, options = s2_options(model = "closed"))
+
+s2_covers(x, y, options = s2_options(model = "closed"))
+
+s2_disjoint(x, y, options = s2_options())
+
+s2_intersects(x, y, options = s2_options())
+
+s2_equals(x, y, options = s2_options())
+
+s2_intersects_box(
+ x,
+ lng1,
+ lat1,
+ lng2,
+ lat2,
+ detail = 1000,
+ options = s2_options()
+)
+
+s2_touches(x, y, options = s2_options())
+
+s2_dwithin(x, y, distance, radius = s2_earth_radius_meters())
+}
+\arguments{
+\item{x}{\link[=as_s2_geography]{geography vectors}. These inputs
+are passed to \code{\link[=as_s2_geography]{as_s2_geography()}}, so you can pass other objects
+(e.g., character vectors of well-known text) directly.}
+
+\item{y}{\link[=as_s2_geography]{geography vectors}. These inputs
+are passed to \code{\link[=as_s2_geography]{as_s2_geography()}}, so you can pass other objects
+(e.g., character vectors of well-known text) directly.}
+
+\item{options}{An \code{\link[=s2_options]{s2_options()}} object describing the polygon/polyline
+model to use and the snap level.}
+
+\item{lng1, lat1, lng2, lat2}{A latitude/longitude range}
+
+\item{detail}{The number of points with which to approximate
+non-geodesic edges.}
+
+\item{distance}{A distance on the surface of the earth in the same units
+as \code{radius}.}
+
+\item{radius}{Radius of the earth. Defaults to the average radius of
+the earth in meters as defined by \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}}.}
+}
+\description{
+These functions operate two geography vectors (pairwise), and return
+a logical vector.
+}
+\section{Model}{
+
+The geometry model indicates whether or not a geometry includes its boundaries.
+Boundaries of line geometries are its end points.
+OPEN geometries do not contain their boundary (\code{model = "open"}); CLOSED
+geometries (\code{model = "closed"}) contain their boundary; SEMI-OPEN geometries
+(\code{model = "semi-open"}) contain half of their boundaries, such that when two polygons
+do not overlap or two lines do not cross, no point exist that belong to
+more than one of the geometries. (This latter form, half-closed, is
+not present in the OpenGIS "simple feature access" (SFA) standard nor DE9-IM on
+which that is based). The default values for \code{\link[=s2_contains]{s2_contains()}} (open)
+and covers/covered_by (closed) correspond to the SFA standard specification
+of these operators.
+}
+
+\examples{
+s2_contains(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ c("POINT (5 5)", "POINT (-1 1)")
+)
+
+s2_within(
+ c("POINT (5 5)", "POINT (-1 1)"),
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))"
+)
+
+s2_covered_by(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ c("POINT (5 5)", "POINT (-1 1)")
+)
+
+s2_covers(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ c("POINT (5 5)", "POINT (-1 1)")
+)
+
+s2_disjoint(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ c("POINT (5 5)", "POINT (-1 1)")
+)
+
+s2_intersects(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ c("POINT (5 5)", "POINT (-1 1)")
+)
+
+s2_equals(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ c(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((10 0, 10 10, 0 10, 0 0, 10 0))",
+ "POLYGON ((-1 -1, 10 0, 10 10, 0 10, -1 -1))"
+ )
+)
+
+s2_intersects(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ c("POINT (5 5)", "POINT (-1 1)")
+)
+
+s2_intersects_box(
+ c("POINT (5 5)", "POINT (-1 1)"),
+ 0, 0, 10, 10
+)
+
+s2_touches(
+ "POLYGON ((0 0, 0 1, 1 1, 0 0))",
+ c("POINT (0 0)", "POINT (0.5 0.75)", "POINT (0 0.5)")
+)
+
+s2_dwithin(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ c("POINT (5 5)", "POINT (-1 1)"),
+ 0 # distance in meters
+)
+
+s2_dwithin(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ c("POINT (5 5)", "POINT (-1 1)"),
+ 1e6 # distance in meters
+)
+
+}
+\seealso{
+Matrix versions of these predicates (e.g., \code{\link[=s2_intersects_matrix]{s2_intersects_matrix()}}).
+
+BigQuery's geography function reference:
+\itemize{
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_contains}{ST_CONTAINS}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_coveredby}{ST_COVEREDBY}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_covers}{ST_COVERS}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_disjoint}{ST_DISJOINT}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_equals}{ST_EQUALS}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_intersects}{ST_INTERSECTS}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_intersectsbox}{ST_INTERSECTSBOX}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_touches}{ST_TOUCHES}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_within}{ST_WITHIN}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_dwithin}{ST_DWITHIN}
+}
+}
--- /dev/null
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/data.R
+\docType{data}
+\name{s2_data_tbl_countries}
+\alias{s2_data_tbl_countries}
+\alias{s2_data_tbl_timezones}
+\alias{s2_data_tbl_cities}
+\alias{s2_data_countries}
+\alias{s2_data_timezones}
+\alias{s2_data_cities}
+\title{Low-resolution world boundaries, timezones, and cities}
+\format{
+A data.frame with columns \code{name} (character), and
+\code{geometry} (wk_wkb)
+
+An object of class \code{data.frame} with 120 rows and 2 columns.
+
+An object of class \code{data.frame} with 243 rows and 3 columns.
+}
+\source{
+\href{https://www.naturalearthdata.com/}{Natural Earth Data}
+}
+\usage{
+s2_data_tbl_countries
+
+s2_data_tbl_timezones
+
+s2_data_tbl_cities
+
+s2_data_countries(name = NULL)
+
+s2_data_timezones(utc_offset_min = NULL, utc_offset_max = utc_offset_min)
+
+s2_data_cities(name = NULL)
+}
+\arguments{
+\item{name}{The name of a country, continent, city, or \code{NULL}
+for all features.}
+
+\item{utc_offset_min, utc_offset_max}{Minimum and/or maximum timezone
+offsets.}
+}
+\description{
+Well-known binary versions of the \href{https://www.naturalearthdata.com/}{Natural Earth}
+low-resolution world boundaries and timezone boundaries.
+}
+\examples{
+head(s2_data_countries())
+s2_data_countries("Germany")
+s2_data_countries("Europe")
+
+head(s2_data_timezones())
+s2_data_timezones(-4)
+
+head(s2_data_cities())
+s2_data_cities("Cairo")
+
+}
+\keyword{datasets}
--- /dev/null
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/s2-earth.R
+\name{s2_earth_radius_meters}
+\alias{s2_earth_radius_meters}
+\title{Earth Constants}
+\usage{
+s2_earth_radius_meters()
+}
+\description{
+According to Yoder (1995), the radius of the earth is
+6371.01 km. These functions are used to set the
+default radis for functions that return a distance
+or accept a distance as input
+(e.g., \code{\link[=s2_distance]{s2_distance()}} and \code{\link[=s2_dwithin]{s2_dwithin()}}).
+}
+\examples{
+s2_earth_radius_meters()
+
+}
+\references{
+Yoder, C.F. 1995. "Astrometric and Geodetic Properties of Earth and the
+Solar System" in Global Earth Physics, A Handbook of Physical Constants,
+AGU Reference Shelf 1, American Geophysical Union, Table 2.
+\doi{10.1029/RF001p0001}
+}
--- /dev/null
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/s2-constructors-formatters.R
+\name{s2_geog_point}
+\alias{s2_geog_point}
+\alias{s2_make_line}
+\alias{s2_make_polygon}
+\alias{s2_geog_from_text}
+\alias{s2_geog_from_wkb}
+\alias{s2_as_text}
+\alias{s2_as_binary}
+\title{Create and Format Geography Vectors}
+\usage{
+s2_geog_point(longitude, latitude)
+
+s2_make_line(longitude, latitude, feature_id = 1L)
+
+s2_make_polygon(
+ longitude,
+ latitude,
+ feature_id = 1L,
+ ring_id = 1L,
+ oriented = FALSE,
+ check = TRUE
+)
+
+s2_geog_from_text(wkt_string, oriented = FALSE, check = TRUE)
+
+s2_geog_from_wkb(wkb_bytes, oriented = FALSE, check = TRUE)
+
+s2_as_text(x, precision = 16, trim = TRUE)
+
+s2_as_binary(x, endian = wk::wk_platform_endian())
+}
+\arguments{
+\item{longitude, latitude}{Vectors of latitude and longitude}
+
+\item{feature_id, ring_id}{Vectors for which a change in
+sequential values indicates a new feature or ring. Use \code{\link[=factor]{factor()}}
+to convert from a character vector.}
+
+\item{oriented}{TRUE if polygon ring directions are known to be correct
+(i.e., exterior rings are defined counter clockwise and interior
+rings are defined clockwise).}
+
+\item{check}{Use \code{check = FALSE} to skip error on invalid geometries}
+
+\item{wkt_string}{Well-known text}
+
+\item{wkb_bytes}{A \code{list()} of \code{raw()}}
+
+\item{x}{\link[=as_s2_geography]{geography vectors}. These inputs
+are passed to \code{\link[=as_s2_geography]{as_s2_geography()}}, so you can pass other objects
+(e.g., character vectors of well-known text) directly.}
+
+\item{precision}{The number of significant digits to export when
+writing well-known text. If \code{trim = FALSE}, the number of
+digits after the decimal place.}
+
+\item{trim}{Should trailing zeroes be included after the decimal place?}
+
+\item{endian}{The endian-ness of the well-known binary. See \code{\link[wk:wkb_translate_wkt]{wk::wkb_translate_wkb()}}.}
+}
+\description{
+These functions create and export \link[=as_s2_geography]{geography vectors}.
+Unlike the BigQuery geography constructors, these functions do not sanitize
+invalid or redundant input using \code{\link[=s2_union]{s2_union()}}. Note that when creating polygons
+using \code{\link[=s2_make_polygon]{s2_make_polygon()}}, rings can be open or closed.
+}
+\examples{
+# create point geographies using coordinate values:
+s2_geog_point(-64, 45)
+
+# create line geographies using coordinate values:
+s2_make_line(c(-64, 8), c(45, 71))
+
+# optionally, separate features using feature_id:
+s2_make_line(
+ c(-64, 8, -27, -27), c(45, 71, 0, 45),
+ feature_id = c(1, 1, 2, 2)
+)
+
+# create polygon geographies using coordinate values:
+# (rings can be open or closed)
+s2_make_polygon(c(-45, 8, 0), c(64, 71, 90))
+
+# optionally, separate rings and/or features using
+# ring_id and/or feature_id
+s2_make_polygon(
+ c(20, 10, 10, 30, 45, 30, 20, 20, 40, 20, 45),
+ c(35, 30, 10, 5, 20, 20, 15, 25, 40, 45, 30),
+ feature_id = c(rep(1, 8), rep(2, 3)),
+ ring_id = c(1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1)
+)
+
+# import and export well-known text
+(geog <- s2_geog_from_text("POINT (-64 45)"))
+s2_as_text(geog)
+
+# import and export well-known binary
+(geog <- s2_geog_from_wkb(wk::as_wkb("POINT (-64 45)")))
+s2_as_binary(geog)
+
+}
+\seealso{
+See \code{\link[=as_s2_geography]{as_s2_geography()}} for other ways to construct geography vectors.
+
+BigQuery's geography function reference:
+\itemize{
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_geogpoint}{ST_GEOGPOINT}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_makeline}{ST_MAKELINE}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_makepolygon}{ST_MAKEPOLYGON}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_geogfromtext}{ST_GEOGFROMTEXT}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_geogfromwkb}{ST_GEOGFROMWKB}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_astext}{ST_ASTEXT}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_asbinary}{ST_ASBINARY}
+}
+}
--- /dev/null
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/s2-accessors.R
+\name{s2_is_collection}
+\alias{s2_is_collection}
+\alias{s2_dimension}
+\alias{s2_num_points}
+\alias{s2_is_empty}
+\alias{s2_area}
+\alias{s2_length}
+\alias{s2_perimeter}
+\alias{s2_x}
+\alias{s2_y}
+\alias{s2_distance}
+\alias{s2_max_distance}
+\title{S2 Geography Accessors}
+\usage{
+s2_is_collection(x)
+
+s2_dimension(x)
+
+s2_num_points(x)
+
+s2_is_empty(x)
+
+s2_area(x, radius = s2_earth_radius_meters())
+
+s2_length(x, radius = s2_earth_radius_meters())
+
+s2_perimeter(x, radius = s2_earth_radius_meters())
+
+s2_x(x)
+
+s2_y(x)
+
+s2_distance(x, y, radius = s2_earth_radius_meters())
+
+s2_max_distance(x, y, radius = s2_earth_radius_meters())
+}
+\arguments{
+\item{x, y}{\link[=as_s2_geography]{geography vectors}. These inputs
+are passed to \code{\link[=as_s2_geography]{as_s2_geography()}}, so you can pass other objects
+(e.g., character vectors of well-known text) directly.}
+
+\item{radius}{Radius of the earth. Defaults to the average radius of
+the earth in meters as defined by \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}}.}
+}
+\description{
+Accessors extract information about \link[=as_s2_geography]{geography vectors}.
+}
+\examples{
+# s2_is_collection() tests for multiple geometries in one feature
+s2_is_collection(c("POINT (-64 45)", "MULTIPOINT ((-64 45), (8 72))"))
+
+# s2_dimension() returns 0 for point, 1 for line, 2 for polygon
+s2_dimension(
+ c(
+ "GEOMETRYCOLLECTION EMPTY",
+ "POINT (-64 45)",
+ "LINESTRING (-64 45, 8 72)",
+ "POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))",
+ "GEOMETRYCOLLECTION (POINT (-64 45), LINESTRING (-64 45, 8 72))"
+ )
+)
+
+# s2_num_points() counts points
+s2_num_points(c("POINT (-64 45)", "LINESTRING (-64 45, 8 72)"))
+
+# s2_is_empty tests for emptiness
+s2_is_empty(c("POINT (-64 45)", "POINT EMPTY"))
+
+# calculate area, length, and perimeter
+s2_area("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))")
+s2_perimeter("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))")
+s2_length(s2_boundary("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))"))
+
+# extract x and y coordinates from points
+s2_x(c("POINT (-64 45)", "POINT EMPTY"))
+s2_y(c("POINT (-64 45)", "POINT EMPTY"))
+
+# calculate minimum and maximum distance between two geometries
+s2_distance(
+ "POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))",
+ "POINT (-64 45)"
+)
+s2_max_distance(
+ "POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))",
+ "POINT (-64 45)"
+)
+
+}
+\seealso{
+BigQuery's geography function reference:
+\itemize{
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_iscollection}{ST_ISCOLLECTION}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_dimension}{ST_DIMENSION}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_numpoints}{ST_NUMPOINTS}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_isempty}{ST_ISEMPTY}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_area}{ST_AREA}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_length}{ST_LENGTH}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_perimeter}{ST_PERIMETER}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_x}{ST_X}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_y}{ST_Y}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_distance}{ST_DISTANCE}
+\item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_maxdistance}{ST_MAXDISTANCE}
+}
+}
--- /dev/null
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/s2-lnglat.R
+\name{s2_lnglat}
+\alias{s2_lnglat}
+\alias{as_s2_lnglat}
+\alias{as_s2_lnglat.s2_lnglat}
+\alias{as_s2_lnglat.s2_point}
+\alias{as_s2_lnglat.s2_geography}
+\alias{as_s2_lnglat.matrix}
+\alias{as.data.frame.s2_lnglat}
+\alias{as.matrix.s2_lnglat}
+\alias{as_wkb.s2_lnglat}
+\alias{as_wkt.s2_lnglat}
+\title{Create an S2 LngLat Vector}
+\usage{
+s2_lnglat(lng, lat)
+
+as_s2_lnglat(x, ...)
+
+\method{as_s2_lnglat}{s2_lnglat}(x, ...)
+
+\method{as_s2_lnglat}{s2_point}(x, ...)
+
+\method{as_s2_lnglat}{s2_geography}(x, ...)
+
+\method{as_s2_lnglat}{matrix}(x, ...)
+
+\method{as.data.frame}{s2_lnglat}(x, ...)
+
+\method{as.matrix}{s2_lnglat}(x, ...)
+
+\method{as_wkb}{s2_lnglat}(x, ...)
+
+\method{as_wkt}{s2_lnglat}(x, ...)
+}
+\arguments{
+\item{lat, lng}{Vectors of latitude and longitude values in degrees.}
+
+\item{x}{A \code{\link[=s2_lnglat]{s2_lnglat()}} vector or an object that can be coerced to one.}
+
+\item{...}{Unused}
+}
+\value{
+An object with class s2_lnglat
+}
+\description{
+This class represents a latitude and longitude on the Earth's surface.
+Most calculations in S2 convert this to a \code{\link[=as_s2_point]{as_s2_point()}}, which is a
+unit vector representation of this value.
+}
+\examples{
+s2_lnglat(45, -64) # Halifax, Nova Scotia!
+as.data.frame(s2_lnglat(45, -64))
+
+}
--- /dev/null
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/s2-options.R
+\name{s2_options}
+\alias{s2_options}
+\alias{s2_snap_identity}
+\alias{s2_snap_level}
+\alias{s2_snap_precision}
+\alias{s2_snap_distance}
+\title{Geography Operation Options}
+\usage{
+s2_options(
+ model = NULL,
+ snap = s2_snap_identity(),
+ snap_radius = -1,
+ duplicate_edges = FALSE,
+ edge_type = "directed",
+ validate = FALSE,
+ polyline_type = "path",
+ polyline_sibling_pairs = "keep",
+ simplify_edge_chains = FALSE,
+ split_crossing_edges = FALSE,
+ idempotent = FALSE
+)
+
+s2_snap_identity()
+
+s2_snap_level(level)
+
+s2_snap_precision(precision)
+
+s2_snap_distance(distance)
+}
+\arguments{
+\item{model}{One of 'open', 'semi-open' (default for polygons),
+or 'closed' (default for polylines). See section 'Model'}
+
+\item{snap}{Use \code{s2_snap_identity()}, \code{s2_snap_distance()}, \code{s2_snap_level()},
+or \code{s2_snap_precision()} to specify how or if coordinate rounding should
+occur.}
+
+\item{snap_radius}{As opposed to the snap function, which specifies
+the maximum distance a vertex should move, the snap radius (in radians) sets
+the minimum distance between vertices of the output that don't cause vertices
+to move more than the distance specified by the snap function. This can be used
+to simplify the result of a boolean operation. Use -1 to specify that any
+minimum distance is acceptable.}
+
+\item{duplicate_edges}{Use \code{TRUE} to keep duplicate edges (e.g., duplicate
+points).}
+
+\item{edge_type}{One of 'directed' (default) or 'undirected'.}
+
+\item{validate}{Use \code{TRUE} to validate the result from the builder.}
+
+\item{polyline_type}{One of 'path' (default) or 'walk'. If 'walk',
+polylines that backtrack are preserved.}
+
+\item{polyline_sibling_pairs}{One of 'discard' (default) or 'keep'.}
+
+\item{simplify_edge_chains}{Use \code{TRUE} to remove vertices that are within
+\code{snap_radius} of the original vertex.}
+
+\item{split_crossing_edges}{Use \code{TRUE} to split crossing polyline edges
+when creating geometries.}
+
+\item{idempotent}{Use \code{FALSE} to apply snap even if snapping is not necessary
+to satisfy vertex constraints.}
+
+\item{level}{A value from 0 to 30 corresponding to the cell level
+at which snapping should occur.}
+
+\item{precision}{A number by which coordinates should be multiplied
+before being rounded. Rounded to the nearest exponent of 10.}
+
+\item{distance}{A distance (in radians) denoting the maximum
+distance a vertex should move in the snapping process.}
+}
+\description{
+These functions specify defaults for options used to perform operations
+and construct geometries. These are used in predicates (e.g., \code{\link[=s2_intersects]{s2_intersects()}}),
+and boolean operations (e.g., \code{\link[=s2_intersection]{s2_intersection()}}) to specify the model for
+containment and how new geometries should be constructed.
+}
+\section{Model}{
+
+The geometry model indicates whether or not a geometry includes its boundaries.
+Boundaries of line geometries are its end points.
+OPEN geometries do not contain their boundary (\code{model = "open"}); CLOSED
+geometries (\code{model = "closed"}) contain their boundary; SEMI-OPEN geometries
+(\code{model = "semi-open"}) contain half of their boundaries, such that when two polygons
+do not overlap or two lines do not cross, no point exist that belong to
+more than one of the geometries. (This latter form, half-closed, is
+not present in the OpenGIS "simple feature access" (SFA) standard nor DE9-IM on
+which that is based). The default values for \code{\link[=s2_contains]{s2_contains()}} (open)
+and covers/covered_by (closed) correspond to the SFA standard specification
+of these operators.
+}
+
+\examples{
+# use s2_options() to specify containment models, snap level
+# layer creation options, and builder options
+s2_options(model = "closed", snap = s2_snap_level(30))
+
+}
--- /dev/null
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/s2-point.R
+\name{s2_point}
+\alias{s2_point}
+\alias{as_s2_point}
+\alias{as_s2_point.s2_point}
+\alias{as_s2_point.s2_lnglat}
+\alias{as_s2_point.s2_geography}
+\alias{as_s2_point.matrix}
+\alias{as.data.frame.s2_point}
+\alias{as.matrix.s2_point}
+\title{Create an S2 Point Vector}
+\usage{
+s2_point(x, y, z)
+
+as_s2_point(x, ...)
+
+\method{as_s2_point}{s2_point}(x, ...)
+
+\method{as_s2_point}{s2_lnglat}(x, ...)
+
+\method{as_s2_point}{s2_geography}(x, ...)
+
+\method{as_s2_point}{matrix}(x, ...)
+
+\method{as.data.frame}{s2_point}(x, ...)
+
+\method{as.matrix}{s2_point}(x, ...)
+}
+\arguments{
+\item{x, y, z}{Vectors of latitude and longitude values in degrees.}
+
+\item{...}{Unused}
+}
+\value{
+An object with class s2_point
+}
+\description{
+In S2 terminology, a "point" is a 3-dimensional unit vector representation
+of an \code{\link[=s2_lnglat]{s2_lnglat()}}. Internally, all s2 objects are stored as
+3-dimensional unit vectors.
+}
+\examples{
+lnglat <- s2_lnglat(-64, 45) # Halifax, Nova Scotia!
+as_s2_point(lnglat)
+as.data.frame(as_s2_point(lnglat))
+
+}
--- /dev/null
+PKG_CPPFLAGS = -I../inst/include
+PKG_LIBS = @libs@
+PKG_CXXFLAGS = @cflags@ -pthread
+CXX_STD = CXX11
+
+OBJECTS = cpp-compat.o \
+ s2-accessors.o \
+ s2-bounds.o \
+ s2-constructors-formatters.o \
+ s2-predicates.o \
+ s2-transformers.o \
+ init.o \
+ RcppExports.o \
+ s2-geography.o \
+ s2-lnglat.o \
+ s2-matrix.o \
+ s2-point.o \
+ s2-xptr.o \
+ s2/base/stringprintf.o \
+ s2/base/strtoint.o \
+ s2/encoded_s2cell_id_vector.o \
+ s2/encoded_s2point_vector.o \
+ s2/encoded_s2shape_index.o \
+ s2/encoded_string_vector.o \
+ s2/id_set_lexicon.o \
+ s2/mutable_s2shape_index.o \
+ s2/r2rect.o \
+ s2/s1angle.o \
+ s2/s1chord_angle.o \
+ s2/s1interval.o \
+ s2/s2boolean_operation.o \
+ s2/s2builder_graph.o \
+ s2/s2builder.o \
+ s2/s2builderutil_closed_set_normalizer.o \
+ s2/s2builderutil_find_polygon_degeneracies.o \
+ s2/s2builderutil_lax_polygon_layer.o \
+ s2/s2builderutil_s2point_vector_layer.o \
+ s2/s2builderutil_s2polygon_layer.o \
+ s2/s2builderutil_s2polyline_layer.o \
+ s2/s2builderutil_s2polyline_vector_layer.o \
+ s2/s2builderutil_snap_functions.o \
+ s2/s2builderutil_testing.o \
+ s2/s2cap.o \
+ s2/s2cell_id.o \
+ s2/s2cell_index.o \
+ s2/s2cell_union.o \
+ s2/s2cell.o \
+ s2/s2centroids.o \
+ s2/s2closest_cell_query.o \
+ s2/s2closest_edge_query.o \
+ s2/s2closest_point_query.o \
+ s2/s2contains_vertex_query.o \
+ s2/s2convex_hull_query.o \
+ s2/s2coords.o \
+ s2/s2crossing_edge_query.o \
+ s2/s2debug.o \
+ s2/s2earth.o \
+ s2/s2edge_clipping.o \
+ s2/s2edge_crosser.o \
+ s2/s2edge_crossings.o \
+ s2/s2edge_distances.o \
+ s2/s2edge_tessellator.o \
+ s2/s2error.o \
+ s2/s2furthest_edge_query.o \
+ s2/s2latlng_rect_bounder.o \
+ s2/s2latlng_rect.o \
+ s2/s2latlng.o \
+ s2/s2lax_loop_shape.o \
+ s2/s2lax_polygon_shape.o \
+ s2/s2lax_polyline_shape.o \
+ s2/s2loop_measures.o \
+ s2/s2loop.o \
+ s2/s2max_distance_targets.o \
+ s2/s2measures.o \
+ s2/s2metrics.o \
+ s2/s2min_distance_targets.o \
+ s2/s2padded_cell.o \
+ s2/s2point_compression.o \
+ s2/s2point_region.o \
+ s2/s2pointutil.o \
+ s2/s2polygon.o \
+ s2/s2polyline_alignment.o \
+ s2/s2polyline_measures.o \
+ s2/s2polyline_simplifier.o \
+ s2/s2polyline.o \
+ s2/s2predicates.o \
+ s2/s2projections.o \
+ s2/s2r2rect.o \
+ s2/s2region_coverer.o \
+ s2/s2region_intersection.o \
+ s2/s2region_term_indexer.o \
+ s2/s2region_union.o \
+ s2/s2region.o \
+ s2/s2shape_index_buffered_region.o \
+ s2/s2shape_index_measures.o \
+ s2/s2shape_index.o \
+ s2/s2shape_measures.o \
+ s2/s2shapeutil_build_polygon_boundaries.o \
+ s2/s2shapeutil_coding.o \
+ s2/s2shapeutil_contains_brute_force.o \
+ s2/s2shapeutil_edge_iterator.o \
+ s2/s2shapeutil_get_reference_point.o \
+ s2/s2shapeutil_range_iterator.o \
+ s2/s2shapeutil_visit_crossing_edge_pairs.o \
+ s2/s2testing.o \
+ s2/s2text_format.o \
+ s2/s2wedge_relations.o \
+ s2/strings/ostringstream.o \
+ s2/strings/serialize.o \
+ s2/third_party/absl/base/dynamic_annotations.o \
+ s2/third_party/absl/base/internal/raw_logging.o \
+ s2/third_party/absl/base/internal/throw_delegate.o \
+ s2/third_party/absl/numeric/int128.o \
+ s2/third_party/absl/strings/ascii.o \
+ s2/third_party/absl/strings/internal/memutil.o \
+ s2/third_party/absl/strings/match.o \
+ s2/third_party/absl/strings/numbers.o \
+ s2/third_party/absl/strings/str_cat.o \
+ s2/third_party/absl/strings/str_split.o \
+ s2/third_party/absl/strings/string_view.o \
+ s2/third_party/absl/strings/strip.o \
+ s2/util/bits/bit-interleave.o \
+ s2/util/bits/bits.o \
+ s2/util/coding/coder.o \
+ s2/util/coding/varint.o \
+ s2/util/math/exactfloat/exactfloat.o \
+ s2/util/math/mathutil.o \
+ s2/util/units/length-units.o
--- /dev/null
+PKG_CPPFLAGS = -DS2_USE_EXACTFLOAT -D_USE_MATH_DEFINES -DNDEBUG -DIS_LITTLE_ENDIAN -I../windows/openssl-1.1.1/include -I../inst/include
+PKG_LIBS = -L../windows/openssl-1.1.1/lib${R_ARCH} -lssl -lcrypto -lcrypt32 -lws2_32
+
+CXX_STD = CXX11
+
+OBJECTS = cpp-compat.o \
+ s2-accessors.o \
+ s2-bounds.o \
+ s2-constructors-formatters.o \
+ s2-predicates.o \
+ s2-transformers.o \
+ init.o \
+ RcppExports.o \
+ s2-geography.o \
+ s2-lnglat.o \
+ s2-matrix.o \
+ s2-point.o \
+ s2-xptr.o \
+ s2/base/stringprintf.o \
+ s2/base/strtoint.o \
+ s2/encoded_s2cell_id_vector.o \
+ s2/encoded_s2point_vector.o \
+ s2/encoded_s2shape_index.o \
+ s2/encoded_string_vector.o \
+ s2/id_set_lexicon.o \
+ s2/mutable_s2shape_index.o \
+ s2/r2rect.o \
+ s2/s1angle.o \
+ s2/s1chord_angle.o \
+ s2/s1interval.o \
+ s2/s2boolean_operation.o \
+ s2/s2builder_graph.o \
+ s2/s2builder.o \
+ s2/s2builderutil_closed_set_normalizer.o \
+ s2/s2builderutil_find_polygon_degeneracies.o \
+ s2/s2builderutil_lax_polygon_layer.o \
+ s2/s2builderutil_s2point_vector_layer.o \
+ s2/s2builderutil_s2polygon_layer.o \
+ s2/s2builderutil_s2polyline_layer.o \
+ s2/s2builderutil_s2polyline_vector_layer.o \
+ s2/s2builderutil_snap_functions.o \
+ s2/s2builderutil_testing.o \
+ s2/s2cap.o \
+ s2/s2cell_id.o \
+ s2/s2cell_index.o \
+ s2/s2cell_union.o \
+ s2/s2cell.o \
+ s2/s2centroids.o \
+ s2/s2closest_cell_query.o \
+ s2/s2closest_edge_query.o \
+ s2/s2closest_point_query.o \
+ s2/s2contains_vertex_query.o \
+ s2/s2convex_hull_query.o \
+ s2/s2coords.o \
+ s2/s2crossing_edge_query.o \
+ s2/s2debug.o \
+ s2/s2earth.o \
+ s2/s2edge_clipping.o \
+ s2/s2edge_crosser.o \
+ s2/s2edge_crossings.o \
+ s2/s2edge_distances.o \
+ s2/s2edge_tessellator.o \
+ s2/s2error.o \
+ s2/s2furthest_edge_query.o \
+ s2/s2latlng_rect_bounder.o \
+ s2/s2latlng_rect.o \
+ s2/s2latlng.o \
+ s2/s2lax_loop_shape.o \
+ s2/s2lax_polygon_shape.o \
+ s2/s2lax_polyline_shape.o \
+ s2/s2loop_measures.o \
+ s2/s2loop.o \
+ s2/s2max_distance_targets.o \
+ s2/s2measures.o \
+ s2/s2metrics.o \
+ s2/s2min_distance_targets.o \
+ s2/s2padded_cell.o \
+ s2/s2point_compression.o \
+ s2/s2point_region.o \
+ s2/s2pointutil.o \
+ s2/s2polygon.o \
+ s2/s2polyline_alignment.o \
+ s2/s2polyline_measures.o \
+ s2/s2polyline_simplifier.o \
+ s2/s2polyline.o \
+ s2/s2predicates.o \
+ s2/s2projections.o \
+ s2/s2r2rect.o \
+ s2/s2region_coverer.o \
+ s2/s2region_intersection.o \
+ s2/s2region_term_indexer.o \
+ s2/s2region_union.o \
+ s2/s2region.o \
+ s2/s2shape_index_buffered_region.o \
+ s2/s2shape_index_measures.o \
+ s2/s2shape_index.o \
+ s2/s2shape_measures.o \
+ s2/s2shapeutil_build_polygon_boundaries.o \
+ s2/s2shapeutil_coding.o \
+ s2/s2shapeutil_contains_brute_force.o \
+ s2/s2shapeutil_edge_iterator.o \
+ s2/s2shapeutil_get_reference_point.o \
+ s2/s2shapeutil_range_iterator.o \
+ s2/s2shapeutil_visit_crossing_edge_pairs.o \
+ s2/s2testing.o \
+ s2/s2text_format.o \
+ s2/s2wedge_relations.o \
+ s2/strings/ostringstream.o \
+ s2/strings/serialize.o \
+ s2/third_party/absl/base/dynamic_annotations.o \
+ s2/third_party/absl/base/internal/raw_logging.o \
+ s2/third_party/absl/base/internal/throw_delegate.o \
+ s2/third_party/absl/numeric/int128.o \
+ s2/third_party/absl/strings/ascii.o \
+ s2/third_party/absl/strings/internal/memutil.o \
+ s2/third_party/absl/strings/match.o \
+ s2/third_party/absl/strings/numbers.o \
+ s2/third_party/absl/strings/str_cat.o \
+ s2/third_party/absl/strings/str_split.o \
+ s2/third_party/absl/strings/string_view.o \
+ s2/third_party/absl/strings/strip.o \
+ s2/util/bits/bit-interleave.o \
+ s2/util/bits/bits.o \
+ s2/util/coding/coder.o \
+ s2/util/coding/varint.o \
+ s2/util/math/exactfloat/exactfloat.o \
+ s2/util/math/mathutil.o \
+ s2/util/units/length-units.o
+
+all: clean winlibs
+
+winlibs:
+ mkdir -p ../inst
+ "${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" --vanilla "../tools/winlibs.R"
+
+clean:
+ rm -f $(SHLIB) $(OBJECTS)
+
+.PHONY: all winlibs clean
--- /dev/null
+// Generated by using Rcpp::compileAttributes() -> do not edit by hand
+// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393
+
+#include <Rcpp.h>
+
+using namespace Rcpp;
+
+// cpp_s2_init
+void cpp_s2_init();
+RcppExport SEXP _s2_cpp_s2_init() {
+BEGIN_RCPP
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ cpp_s2_init();
+ return R_NilValue;
+END_RCPP
+}
+// cpp_s2_is_collection
+LogicalVector cpp_s2_is_collection(List geog);
+RcppExport SEXP _s2_cpp_s2_is_collection(SEXP geogSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog(geogSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_is_collection(geog));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_dimension
+IntegerVector cpp_s2_dimension(List geog);
+RcppExport SEXP _s2_cpp_s2_dimension(SEXP geogSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog(geogSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_dimension(geog));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_num_points
+IntegerVector cpp_s2_num_points(List geog);
+RcppExport SEXP _s2_cpp_s2_num_points(SEXP geogSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog(geogSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_num_points(geog));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_is_empty
+LogicalVector cpp_s2_is_empty(List geog);
+RcppExport SEXP _s2_cpp_s2_is_empty(SEXP geogSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog(geogSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_is_empty(geog));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_area
+NumericVector cpp_s2_area(List geog);
+RcppExport SEXP _s2_cpp_s2_area(SEXP geogSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog(geogSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_area(geog));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_length
+NumericVector cpp_s2_length(List geog);
+RcppExport SEXP _s2_cpp_s2_length(SEXP geogSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog(geogSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_length(geog));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_perimeter
+NumericVector cpp_s2_perimeter(List geog);
+RcppExport SEXP _s2_cpp_s2_perimeter(SEXP geogSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog(geogSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_perimeter(geog));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_x
+NumericVector cpp_s2_x(List geog);
+RcppExport SEXP _s2_cpp_s2_x(SEXP geogSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog(geogSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_x(geog));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_y
+NumericVector cpp_s2_y(List geog);
+RcppExport SEXP _s2_cpp_s2_y(SEXP geogSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog(geogSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_y(geog));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_distance
+NumericVector cpp_s2_distance(List geog1, List geog2);
+RcppExport SEXP _s2_cpp_s2_distance(SEXP geog1SEXP, SEXP geog2SEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_distance(geog1, geog2));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_max_distance
+NumericVector cpp_s2_max_distance(List geog1, List geog2);
+RcppExport SEXP _s2_cpp_s2_max_distance(SEXP geog1SEXP, SEXP geog2SEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_max_distance(geog1, geog2));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_bounds_cap
+DataFrame cpp_s2_bounds_cap(List geog);
+RcppExport SEXP _s2_cpp_s2_bounds_cap(SEXP geogSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog(geogSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_bounds_cap(geog));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_bounds_rect
+DataFrame cpp_s2_bounds_rect(List geog);
+RcppExport SEXP _s2_cpp_s2_bounds_rect(SEXP geogSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog(geogSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_bounds_rect(geog));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_geog_point
+List cpp_s2_geog_point(NumericVector x, NumericVector y);
+RcppExport SEXP _s2_cpp_s2_geog_point(SEXP xSEXP, SEXP ySEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< NumericVector >::type x(xSEXP);
+ Rcpp::traits::input_parameter< NumericVector >::type y(ySEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_geog_point(x, y));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_make_line
+List cpp_s2_make_line(NumericVector x, NumericVector y, IntegerVector featureId);
+RcppExport SEXP _s2_cpp_s2_make_line(SEXP xSEXP, SEXP ySEXP, SEXP featureIdSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< NumericVector >::type x(xSEXP);
+ Rcpp::traits::input_parameter< NumericVector >::type y(ySEXP);
+ Rcpp::traits::input_parameter< IntegerVector >::type featureId(featureIdSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_make_line(x, y, featureId));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_make_polygon
+List cpp_s2_make_polygon(NumericVector x, NumericVector y, IntegerVector featureId, IntegerVector ringId, bool oriented, bool check);
+RcppExport SEXP _s2_cpp_s2_make_polygon(SEXP xSEXP, SEXP ySEXP, SEXP featureIdSEXP, SEXP ringIdSEXP, SEXP orientedSEXP, SEXP checkSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< NumericVector >::type x(xSEXP);
+ Rcpp::traits::input_parameter< NumericVector >::type y(ySEXP);
+ Rcpp::traits::input_parameter< IntegerVector >::type featureId(featureIdSEXP);
+ Rcpp::traits::input_parameter< IntegerVector >::type ringId(ringIdSEXP);
+ Rcpp::traits::input_parameter< bool >::type oriented(orientedSEXP);
+ Rcpp::traits::input_parameter< bool >::type check(checkSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_make_polygon(x, y, featureId, ringId, oriented, check));
+ return rcpp_result_gen;
+END_RCPP
+}
+// s2_geography_from_wkb
+List s2_geography_from_wkb(List wkb, bool oriented, bool check);
+RcppExport SEXP _s2_s2_geography_from_wkb(SEXP wkbSEXP, SEXP orientedSEXP, SEXP checkSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type wkb(wkbSEXP);
+ Rcpp::traits::input_parameter< bool >::type oriented(orientedSEXP);
+ Rcpp::traits::input_parameter< bool >::type check(checkSEXP);
+ rcpp_result_gen = Rcpp::wrap(s2_geography_from_wkb(wkb, oriented, check));
+ return rcpp_result_gen;
+END_RCPP
+}
+// s2_geography_from_wkt
+List s2_geography_from_wkt(CharacterVector wkt, bool oriented, bool check);
+RcppExport SEXP _s2_s2_geography_from_wkt(SEXP wktSEXP, SEXP orientedSEXP, SEXP checkSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< CharacterVector >::type wkt(wktSEXP);
+ Rcpp::traits::input_parameter< bool >::type oriented(orientedSEXP);
+ Rcpp::traits::input_parameter< bool >::type check(checkSEXP);
+ rcpp_result_gen = Rcpp::wrap(s2_geography_from_wkt(wkt, oriented, check));
+ return rcpp_result_gen;
+END_RCPP
+}
+// s2_geography_full
+List s2_geography_full(LogicalVector x);
+RcppExport SEXP _s2_s2_geography_full(SEXP xSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< LogicalVector >::type x(xSEXP);
+ rcpp_result_gen = Rcpp::wrap(s2_geography_full(x));
+ return rcpp_result_gen;
+END_RCPP
+}
+// s2_geography_to_wkt
+CharacterVector s2_geography_to_wkt(List s2_geography, int precision, bool trim);
+RcppExport SEXP _s2_s2_geography_to_wkt(SEXP s2_geographySEXP, SEXP precisionSEXP, SEXP trimSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type s2_geography(s2_geographySEXP);
+ Rcpp::traits::input_parameter< int >::type precision(precisionSEXP);
+ Rcpp::traits::input_parameter< bool >::type trim(trimSEXP);
+ rcpp_result_gen = Rcpp::wrap(s2_geography_to_wkt(s2_geography, precision, trim));
+ return rcpp_result_gen;
+END_RCPP
+}
+// s2_geography_to_wkb
+List s2_geography_to_wkb(List s2_geography, int endian);
+RcppExport SEXP _s2_s2_geography_to_wkb(SEXP s2_geographySEXP, SEXP endianSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type s2_geography(s2_geographySEXP);
+ Rcpp::traits::input_parameter< int >::type endian(endianSEXP);
+ rcpp_result_gen = Rcpp::wrap(s2_geography_to_wkb(s2_geography, endian));
+ return rcpp_result_gen;
+END_RCPP
+}
+// s2_geography_format
+CharacterVector s2_geography_format(List s2_geography, int maxCoords, int precision, bool trim);
+RcppExport SEXP _s2_s2_geography_format(SEXP s2_geographySEXP, SEXP maxCoordsSEXP, SEXP precisionSEXP, SEXP trimSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type s2_geography(s2_geographySEXP);
+ Rcpp::traits::input_parameter< int >::type maxCoords(maxCoordsSEXP);
+ Rcpp::traits::input_parameter< int >::type precision(precisionSEXP);
+ Rcpp::traits::input_parameter< bool >::type trim(trimSEXP);
+ rcpp_result_gen = Rcpp::wrap(s2_geography_format(s2_geography, maxCoords, precision, trim));
+ return rcpp_result_gen;
+END_RCPP
+}
+// s2_lnglat_from_numeric
+List s2_lnglat_from_numeric(NumericVector lng, NumericVector lat);
+RcppExport SEXP _s2_s2_lnglat_from_numeric(SEXP lngSEXP, SEXP latSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< NumericVector >::type lng(lngSEXP);
+ Rcpp::traits::input_parameter< NumericVector >::type lat(latSEXP);
+ rcpp_result_gen = Rcpp::wrap(s2_lnglat_from_numeric(lng, lat));
+ return rcpp_result_gen;
+END_RCPP
+}
+// s2_lnglat_from_s2_point
+List s2_lnglat_from_s2_point(List s2_point);
+RcppExport SEXP _s2_s2_lnglat_from_s2_point(SEXP s2_pointSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type s2_point(s2_pointSEXP);
+ rcpp_result_gen = Rcpp::wrap(s2_lnglat_from_s2_point(s2_point));
+ return rcpp_result_gen;
+END_RCPP
+}
+// data_frame_from_s2_lnglat
+List data_frame_from_s2_lnglat(List xptr);
+RcppExport SEXP _s2_data_frame_from_s2_lnglat(SEXP xptrSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type xptr(xptrSEXP);
+ rcpp_result_gen = Rcpp::wrap(data_frame_from_s2_lnglat(xptr));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_closest_feature
+IntegerVector cpp_s2_closest_feature(List geog1, List geog2);
+RcppExport SEXP _s2_cpp_s2_closest_feature(SEXP geog1SEXP, SEXP geog2SEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_closest_feature(geog1, geog2));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_farthest_feature
+IntegerVector cpp_s2_farthest_feature(List geog1, List geog2);
+RcppExport SEXP _s2_cpp_s2_farthest_feature(SEXP geog1SEXP, SEXP geog2SEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_farthest_feature(geog1, geog2));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_may_intersect_matrix
+List cpp_s2_may_intersect_matrix(List geog1, List geog2, int maxEdgesPerCell, int maxFeatureCells, List s2options);
+RcppExport SEXP _s2_cpp_s2_may_intersect_matrix(SEXP geog1SEXP, SEXP geog2SEXP, SEXP maxEdgesPerCellSEXP, SEXP maxFeatureCellsSEXP, SEXP s2optionsSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ Rcpp::traits::input_parameter< int >::type maxEdgesPerCell(maxEdgesPerCellSEXP);
+ Rcpp::traits::input_parameter< int >::type maxFeatureCells(maxFeatureCellsSEXP);
+ Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_may_intersect_matrix(geog1, geog2, maxEdgesPerCell, maxFeatureCells, s2options));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_contains_matrix
+List cpp_s2_contains_matrix(List geog1, List geog2, List s2options);
+RcppExport SEXP _s2_cpp_s2_contains_matrix(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_contains_matrix(geog1, geog2, s2options));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_within_matrix
+List cpp_s2_within_matrix(List geog1, List geog2, List s2options);
+RcppExport SEXP _s2_cpp_s2_within_matrix(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_within_matrix(geog1, geog2, s2options));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_intersects_matrix
+List cpp_s2_intersects_matrix(List geog1, List geog2, List s2options);
+RcppExport SEXP _s2_cpp_s2_intersects_matrix(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_intersects_matrix(geog1, geog2, s2options));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_equals_matrix
+List cpp_s2_equals_matrix(List geog1, List geog2, List s2options);
+RcppExport SEXP _s2_cpp_s2_equals_matrix(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_equals_matrix(geog1, geog2, s2options));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_touches_matrix
+List cpp_s2_touches_matrix(List geog1, List geog2, List s2options);
+RcppExport SEXP _s2_cpp_s2_touches_matrix(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_touches_matrix(geog1, geog2, s2options));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_dwithin_matrix
+List cpp_s2_dwithin_matrix(List geog1, List geog2, double distance);
+RcppExport SEXP _s2_cpp_s2_dwithin_matrix(SEXP geog1SEXP, SEXP geog2SEXP, SEXP distanceSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ Rcpp::traits::input_parameter< double >::type distance(distanceSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_dwithin_matrix(geog1, geog2, distance));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_distance_matrix
+NumericMatrix cpp_s2_distance_matrix(List geog1, List geog2);
+RcppExport SEXP _s2_cpp_s2_distance_matrix(SEXP geog1SEXP, SEXP geog2SEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_distance_matrix(geog1, geog2));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_max_distance_matrix
+NumericMatrix cpp_s2_max_distance_matrix(List geog1, List geog2);
+RcppExport SEXP _s2_cpp_s2_max_distance_matrix(SEXP geog1SEXP, SEXP geog2SEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_max_distance_matrix(geog1, geog2));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_contains_matrix_brute_force
+List cpp_s2_contains_matrix_brute_force(List geog1, List geog2, List s2options);
+RcppExport SEXP _s2_cpp_s2_contains_matrix_brute_force(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_contains_matrix_brute_force(geog1, geog2, s2options));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_within_matrix_brute_force
+List cpp_s2_within_matrix_brute_force(List geog1, List geog2, List s2options);
+RcppExport SEXP _s2_cpp_s2_within_matrix_brute_force(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_within_matrix_brute_force(geog1, geog2, s2options));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_intersects_matrix_brute_force
+List cpp_s2_intersects_matrix_brute_force(List geog1, List geog2, List s2options);
+RcppExport SEXP _s2_cpp_s2_intersects_matrix_brute_force(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_intersects_matrix_brute_force(geog1, geog2, s2options));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_disjoint_matrix_brute_force
+List cpp_s2_disjoint_matrix_brute_force(List geog1, List geog2, List s2options);
+RcppExport SEXP _s2_cpp_s2_disjoint_matrix_brute_force(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_disjoint_matrix_brute_force(geog1, geog2, s2options));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_equals_matrix_brute_force
+List cpp_s2_equals_matrix_brute_force(List geog1, List geog2, List s2options);
+RcppExport SEXP _s2_cpp_s2_equals_matrix_brute_force(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_equals_matrix_brute_force(geog1, geog2, s2options));
+ return rcpp_result_gen;
+END_RCPP
+}
+// s2_point_from_numeric
+List s2_point_from_numeric(NumericVector x, NumericVector y, NumericVector z);
+RcppExport SEXP _s2_s2_point_from_numeric(SEXP xSEXP, SEXP ySEXP, SEXP zSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< NumericVector >::type x(xSEXP);
+ Rcpp::traits::input_parameter< NumericVector >::type y(ySEXP);
+ Rcpp::traits::input_parameter< NumericVector >::type z(zSEXP);
+ rcpp_result_gen = Rcpp::wrap(s2_point_from_numeric(x, y, z));
+ return rcpp_result_gen;
+END_RCPP
+}
+// s2_point_from_s2_lnglat
+List s2_point_from_s2_lnglat(List s2_lnglat);
+RcppExport SEXP _s2_s2_point_from_s2_lnglat(SEXP s2_lnglatSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type s2_lnglat(s2_lnglatSEXP);
+ rcpp_result_gen = Rcpp::wrap(s2_point_from_s2_lnglat(s2_lnglat));
+ return rcpp_result_gen;
+END_RCPP
+}
+// data_frame_from_s2_point
+List data_frame_from_s2_point(List s2_point);
+RcppExport SEXP _s2_data_frame_from_s2_point(SEXP s2_pointSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type s2_point(s2_pointSEXP);
+ rcpp_result_gen = Rcpp::wrap(data_frame_from_s2_point(s2_point));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_intersects
+LogicalVector cpp_s2_intersects(List geog1, List geog2, List s2options);
+RcppExport SEXP _s2_cpp_s2_intersects(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_intersects(geog1, geog2, s2options));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_equals
+LogicalVector cpp_s2_equals(List geog1, List geog2, List s2options);
+RcppExport SEXP _s2_cpp_s2_equals(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_equals(geog1, geog2, s2options));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_contains
+LogicalVector cpp_s2_contains(List geog1, List geog2, List s2options);
+RcppExport SEXP _s2_cpp_s2_contains(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_contains(geog1, geog2, s2options));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_touches
+LogicalVector cpp_s2_touches(List geog1, List geog2, List s2options);
+RcppExport SEXP _s2_cpp_s2_touches(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_touches(geog1, geog2, s2options));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_dwithin
+LogicalVector cpp_s2_dwithin(List geog1, List geog2, NumericVector distance);
+RcppExport SEXP _s2_cpp_s2_dwithin(SEXP geog1SEXP, SEXP geog2SEXP, SEXP distanceSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ Rcpp::traits::input_parameter< NumericVector >::type distance(distanceSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_dwithin(geog1, geog2, distance));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_intersects_box
+LogicalVector cpp_s2_intersects_box(List geog, NumericVector lng1, NumericVector lat1, NumericVector lng2, NumericVector lat2, IntegerVector detail, List s2options);
+RcppExport SEXP _s2_cpp_s2_intersects_box(SEXP geogSEXP, SEXP lng1SEXP, SEXP lat1SEXP, SEXP lng2SEXP, SEXP lat2SEXP, SEXP detailSEXP, SEXP s2optionsSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog(geogSEXP);
+ Rcpp::traits::input_parameter< NumericVector >::type lng1(lng1SEXP);
+ Rcpp::traits::input_parameter< NumericVector >::type lat1(lat1SEXP);
+ Rcpp::traits::input_parameter< NumericVector >::type lng2(lng2SEXP);
+ Rcpp::traits::input_parameter< NumericVector >::type lat2(lat2SEXP);
+ Rcpp::traits::input_parameter< IntegerVector >::type detail(detailSEXP);
+ Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_intersects_box(geog, lng1, lat1, lng2, lat2, detail, s2options));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_intersection
+List cpp_s2_intersection(List geog1, List geog2, List s2options);
+RcppExport SEXP _s2_cpp_s2_intersection(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_intersection(geog1, geog2, s2options));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_union
+List cpp_s2_union(List geog1, List geog2, List s2options);
+RcppExport SEXP _s2_cpp_s2_union(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_union(geog1, geog2, s2options));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_difference
+List cpp_s2_difference(List geog1, List geog2, List s2options);
+RcppExport SEXP _s2_cpp_s2_difference(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_difference(geog1, geog2, s2options));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_sym_difference
+List cpp_s2_sym_difference(List geog1, List geog2, List s2options);
+RcppExport SEXP _s2_cpp_s2_sym_difference(SEXP geog1SEXP, SEXP geog2SEXP, SEXP s2optionsSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_sym_difference(geog1, geog2, s2options));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_union_agg
+List cpp_s2_union_agg(List geog, List s2options, bool naRm);
+RcppExport SEXP _s2_cpp_s2_union_agg(SEXP geogSEXP, SEXP s2optionsSEXP, SEXP naRmSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog(geogSEXP);
+ Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP);
+ Rcpp::traits::input_parameter< bool >::type naRm(naRmSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_union_agg(geog, s2options, naRm));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_centroid_agg
+List cpp_s2_centroid_agg(List geog, bool naRm);
+RcppExport SEXP _s2_cpp_s2_centroid_agg(SEXP geogSEXP, SEXP naRmSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog(geogSEXP);
+ Rcpp::traits::input_parameter< bool >::type naRm(naRmSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_centroid_agg(geog, naRm));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_closest_point
+List cpp_s2_closest_point(List geog1, List geog2);
+RcppExport SEXP _s2_cpp_s2_closest_point(SEXP geog1SEXP, SEXP geog2SEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_closest_point(geog1, geog2));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_minimum_clearance_line_between
+List cpp_s2_minimum_clearance_line_between(List geog1, List geog2);
+RcppExport SEXP _s2_cpp_s2_minimum_clearance_line_between(SEXP geog1SEXP, SEXP geog2SEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog1(geog1SEXP);
+ Rcpp::traits::input_parameter< List >::type geog2(geog2SEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_minimum_clearance_line_between(geog1, geog2));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_centroid
+List cpp_s2_centroid(List geog);
+RcppExport SEXP _s2_cpp_s2_centroid(SEXP geogSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog(geogSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_centroid(geog));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_boundary
+List cpp_s2_boundary(List geog);
+RcppExport SEXP _s2_cpp_s2_boundary(SEXP geogSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog(geogSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_boundary(geog));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_rebuild
+List cpp_s2_rebuild(List geog, List s2options);
+RcppExport SEXP _s2_cpp_s2_rebuild(SEXP geogSEXP, SEXP s2optionsSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog(geogSEXP);
+ Rcpp::traits::input_parameter< List >::type s2options(s2optionsSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_rebuild(geog, s2options));
+ return rcpp_result_gen;
+END_RCPP
+}
+// cpp_s2_buffer_cells
+List cpp_s2_buffer_cells(List geog, NumericVector distance, int maxCells, int minLevel);
+RcppExport SEXP _s2_cpp_s2_buffer_cells(SEXP geogSEXP, SEXP distanceSEXP, SEXP maxCellsSEXP, SEXP minLevelSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type geog(geogSEXP);
+ Rcpp::traits::input_parameter< NumericVector >::type distance(distanceSEXP);
+ Rcpp::traits::input_parameter< int >::type maxCells(maxCellsSEXP);
+ Rcpp::traits::input_parameter< int >::type minLevel(minLevelSEXP);
+ rcpp_result_gen = Rcpp::wrap(cpp_s2_buffer_cells(geog, distance, maxCells, minLevel));
+ return rcpp_result_gen;
+END_RCPP
+}
+// s2_xptr_test
+List s2_xptr_test(R_xlen_t size);
+RcppExport SEXP _s2_s2_xptr_test(SEXP sizeSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< R_xlen_t >::type size(sizeSEXP);
+ rcpp_result_gen = Rcpp::wrap(s2_xptr_test(size));
+ return rcpp_result_gen;
+END_RCPP
+}
+// s2_xptr_test_op
+void s2_xptr_test_op(List s2_xptr_test);
+RcppExport SEXP _s2_s2_xptr_test_op(SEXP s2_xptr_testSEXP) {
+BEGIN_RCPP
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< List >::type s2_xptr_test(s2_xptr_testSEXP);
+ s2_xptr_test_op(s2_xptr_test);
+ return R_NilValue;
+END_RCPP
+}
+
+static const R_CallMethodDef CallEntries[] = {
+ {"_s2_cpp_s2_init", (DL_FUNC) &_s2_cpp_s2_init, 0},
+ {"_s2_cpp_s2_is_collection", (DL_FUNC) &_s2_cpp_s2_is_collection, 1},
+ {"_s2_cpp_s2_dimension", (DL_FUNC) &_s2_cpp_s2_dimension, 1},
+ {"_s2_cpp_s2_num_points", (DL_FUNC) &_s2_cpp_s2_num_points, 1},
+ {"_s2_cpp_s2_is_empty", (DL_FUNC) &_s2_cpp_s2_is_empty, 1},
+ {"_s2_cpp_s2_area", (DL_FUNC) &_s2_cpp_s2_area, 1},
+ {"_s2_cpp_s2_length", (DL_FUNC) &_s2_cpp_s2_length, 1},
+ {"_s2_cpp_s2_perimeter", (DL_FUNC) &_s2_cpp_s2_perimeter, 1},
+ {"_s2_cpp_s2_x", (DL_FUNC) &_s2_cpp_s2_x, 1},
+ {"_s2_cpp_s2_y", (DL_FUNC) &_s2_cpp_s2_y, 1},
+ {"_s2_cpp_s2_distance", (DL_FUNC) &_s2_cpp_s2_distance, 2},
+ {"_s2_cpp_s2_max_distance", (DL_FUNC) &_s2_cpp_s2_max_distance, 2},
+ {"_s2_cpp_s2_bounds_cap", (DL_FUNC) &_s2_cpp_s2_bounds_cap, 1},
+ {"_s2_cpp_s2_bounds_rect", (DL_FUNC) &_s2_cpp_s2_bounds_rect, 1},
+ {"_s2_cpp_s2_geog_point", (DL_FUNC) &_s2_cpp_s2_geog_point, 2},
+ {"_s2_cpp_s2_make_line", (DL_FUNC) &_s2_cpp_s2_make_line, 3},
+ {"_s2_cpp_s2_make_polygon", (DL_FUNC) &_s2_cpp_s2_make_polygon, 6},
+ {"_s2_s2_geography_from_wkb", (DL_FUNC) &_s2_s2_geography_from_wkb, 3},
+ {"_s2_s2_geography_from_wkt", (DL_FUNC) &_s2_s2_geography_from_wkt, 3},
+ {"_s2_s2_geography_full", (DL_FUNC) &_s2_s2_geography_full, 1},
+ {"_s2_s2_geography_to_wkt", (DL_FUNC) &_s2_s2_geography_to_wkt, 3},
+ {"_s2_s2_geography_to_wkb", (DL_FUNC) &_s2_s2_geography_to_wkb, 2},
+ {"_s2_s2_geography_format", (DL_FUNC) &_s2_s2_geography_format, 4},
+ {"_s2_s2_lnglat_from_numeric", (DL_FUNC) &_s2_s2_lnglat_from_numeric, 2},
+ {"_s2_s2_lnglat_from_s2_point", (DL_FUNC) &_s2_s2_lnglat_from_s2_point, 1},
+ {"_s2_data_frame_from_s2_lnglat", (DL_FUNC) &_s2_data_frame_from_s2_lnglat, 1},
+ {"_s2_cpp_s2_closest_feature", (DL_FUNC) &_s2_cpp_s2_closest_feature, 2},
+ {"_s2_cpp_s2_farthest_feature", (DL_FUNC) &_s2_cpp_s2_farthest_feature, 2},
+ {"_s2_cpp_s2_may_intersect_matrix", (DL_FUNC) &_s2_cpp_s2_may_intersect_matrix, 5},
+ {"_s2_cpp_s2_contains_matrix", (DL_FUNC) &_s2_cpp_s2_contains_matrix, 3},
+ {"_s2_cpp_s2_within_matrix", (DL_FUNC) &_s2_cpp_s2_within_matrix, 3},
+ {"_s2_cpp_s2_intersects_matrix", (DL_FUNC) &_s2_cpp_s2_intersects_matrix, 3},
+ {"_s2_cpp_s2_equals_matrix", (DL_FUNC) &_s2_cpp_s2_equals_matrix, 3},
+ {"_s2_cpp_s2_touches_matrix", (DL_FUNC) &_s2_cpp_s2_touches_matrix, 3},
+ {"_s2_cpp_s2_dwithin_matrix", (DL_FUNC) &_s2_cpp_s2_dwithin_matrix, 3},
+ {"_s2_cpp_s2_distance_matrix", (DL_FUNC) &_s2_cpp_s2_distance_matrix, 2},
+ {"_s2_cpp_s2_max_distance_matrix", (DL_FUNC) &_s2_cpp_s2_max_distance_matrix, 2},
+ {"_s2_cpp_s2_contains_matrix_brute_force", (DL_FUNC) &_s2_cpp_s2_contains_matrix_brute_force, 3},
+ {"_s2_cpp_s2_within_matrix_brute_force", (DL_FUNC) &_s2_cpp_s2_within_matrix_brute_force, 3},
+ {"_s2_cpp_s2_intersects_matrix_brute_force", (DL_FUNC) &_s2_cpp_s2_intersects_matrix_brute_force, 3},
+ {"_s2_cpp_s2_disjoint_matrix_brute_force", (DL_FUNC) &_s2_cpp_s2_disjoint_matrix_brute_force, 3},
+ {"_s2_cpp_s2_equals_matrix_brute_force", (DL_FUNC) &_s2_cpp_s2_equals_matrix_brute_force, 3},
+ {"_s2_s2_point_from_numeric", (DL_FUNC) &_s2_s2_point_from_numeric, 3},
+ {"_s2_s2_point_from_s2_lnglat", (DL_FUNC) &_s2_s2_point_from_s2_lnglat, 1},
+ {"_s2_data_frame_from_s2_point", (DL_FUNC) &_s2_data_frame_from_s2_point, 1},
+ {"_s2_cpp_s2_intersects", (DL_FUNC) &_s2_cpp_s2_intersects, 3},
+ {"_s2_cpp_s2_equals", (DL_FUNC) &_s2_cpp_s2_equals, 3},
+ {"_s2_cpp_s2_contains", (DL_FUNC) &_s2_cpp_s2_contains, 3},
+ {"_s2_cpp_s2_touches", (DL_FUNC) &_s2_cpp_s2_touches, 3},
+ {"_s2_cpp_s2_dwithin", (DL_FUNC) &_s2_cpp_s2_dwithin, 3},
+ {"_s2_cpp_s2_intersects_box", (DL_FUNC) &_s2_cpp_s2_intersects_box, 7},
+ {"_s2_cpp_s2_intersection", (DL_FUNC) &_s2_cpp_s2_intersection, 3},
+ {"_s2_cpp_s2_union", (DL_FUNC) &_s2_cpp_s2_union, 3},
+ {"_s2_cpp_s2_difference", (DL_FUNC) &_s2_cpp_s2_difference, 3},
+ {"_s2_cpp_s2_sym_difference", (DL_FUNC) &_s2_cpp_s2_sym_difference, 3},
+ {"_s2_cpp_s2_union_agg", (DL_FUNC) &_s2_cpp_s2_union_agg, 3},
+ {"_s2_cpp_s2_centroid_agg", (DL_FUNC) &_s2_cpp_s2_centroid_agg, 2},
+ {"_s2_cpp_s2_closest_point", (DL_FUNC) &_s2_cpp_s2_closest_point, 2},
+ {"_s2_cpp_s2_minimum_clearance_line_between", (DL_FUNC) &_s2_cpp_s2_minimum_clearance_line_between, 2},
+ {"_s2_cpp_s2_centroid", (DL_FUNC) &_s2_cpp_s2_centroid, 1},
+ {"_s2_cpp_s2_boundary", (DL_FUNC) &_s2_cpp_s2_boundary, 1},
+ {"_s2_cpp_s2_rebuild", (DL_FUNC) &_s2_cpp_s2_rebuild, 2},
+ {"_s2_cpp_s2_buffer_cells", (DL_FUNC) &_s2_cpp_s2_buffer_cells, 4},
+ {"_s2_s2_xptr_test", (DL_FUNC) &_s2_s2_xptr_test, 1},
+ {"_s2_s2_xptr_test_op", (DL_FUNC) &_s2_s2_xptr_test_op, 1},
+ {NULL, NULL, 0}
+};
+
+RcppExport void R_init_s2(DllInfo *dll) {
+ R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);
+ R_useDynamicSymbols(dll, FALSE);
+}
--- /dev/null
+
+#include "cpp-compat.h"
+#include <Rcpp.h>
+#include <Rmath.h>
+using namespace Rcpp;
+
+#include <stdarg.h> // va_ stuff
+
+void cpp_compat_printf(const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ Rprintf(fmt, args);
+ va_end(args);
+}
+
+void cpp_compat_abort() {
+ throw std::runtime_error("abort() called");
+}
+
+void cpp_compat_exit(int code) {
+ throw std::runtime_error("exit() called");
+}
+
+int cpp_compat_random() {
+ // trying to match what random() would return
+ // www.gnu.org/software/libc/manual/html_node/BSD-Random.html#BSD-Random
+ // the RNG state is correctly managed for functions that use
+ // Rcpp::export...other functions will require management of the RNGScope
+ return unif_rand() * INT_MAX;
+}
+
+void cpp_compat_srandom(int seed) {
+ // pretty sure this should not have any effect
+ // it gets called on load here with the initiation
+ // of the Random class in s2testing, so it can't
+ // error out
+}
+
+std::ostream& cpp_compat_cerr = Rcerr;
+std::ostream& cpp_compat_cout = Rcout;
--- /dev/null
+
+#ifndef GEOGRAPHY_COLLECTION_H
+#define GEOGRAPHY_COLLECTION_H
+
+#include <algorithm>
+#include "geography.h"
+
+// This class handles collections of other Geography
+// objects.
+class GeographyCollection: public Geography {
+public:
+ GeographyCollection(): features(0) {}
+ GeographyCollection(std::vector<std::unique_ptr<Geography>> features):
+ features(std::move(features)) {}
+
+ bool IsCollection() {
+ return this->features.size() > 0;
+ }
+
+ int Dimension() {
+ int dimension = -1;
+ for (size_t i = 0; i < this->features.size(); i++) {
+ dimension = std::max<int>(this->features[i]->Dimension(), dimension);
+ }
+
+ return dimension;
+ }
+
+ int NumPoints() {
+ int numPoints = 0;
+ for (size_t i = 0; i < this->features.size(); i++) {
+ numPoints += this->features[i]->NumPoints();
+ }
+ return numPoints;
+ }
+
+ bool IsEmpty() {
+ // could also loop and test all(!IsEmpty()), but
+ // that is inconsistent with what gets printed
+ return this->features.size() == 0;
+ }
+
+ double Area() {
+ double area = 0;
+ for (size_t i = 0; i < this->features.size(); i++) {
+ area += this->features[i]->Area();
+ }
+ return area;
+ }
+
+ double Length() {
+ double length = 0;
+ for (size_t i = 0; i < this->features.size(); i++) {
+ length += this->features[i]->Length();
+ }
+ return length;
+ }
+
+ double Perimeter() {
+ double perimeter = 0;
+ for (size_t i = 0; i < this->features.size(); i++) {
+ perimeter += this->features[i]->Perimeter();
+ }
+ return perimeter;
+ }
+
+ double X() {
+ Rcpp::stop("Can't compute X value of a non-point geography");
+ }
+
+ double Y() {
+ Rcpp::stop("Can't compute Y value of a non-point geography");
+ }
+
+ S2Point Centroid() {
+ S2Point cumCentroid(0, 0, 0);
+ for (size_t i = 0; i < this->features.size(); i++) {
+ S2Point centroid = this->features[i]->Centroid();
+ if (centroid.Norm2() > 0) {
+ cumCentroid += centroid.Normalize();
+ }
+ }
+
+ return cumCentroid;
+ }
+
+ std::unique_ptr<Geography> Boundary() {
+ std::vector<std::unique_ptr<Geography>> featureBoundaries(this->features.size());
+ for (size_t i = 0; i < this->features.size(); i++) {
+ featureBoundaries[i] = this->features[i]->Boundary();
+ }
+
+ return absl::make_unique<GeographyCollection>(std::move(featureBoundaries));
+ }
+
+ std::vector<int> BuildShapeIndex(MutableS2ShapeIndex* index) {
+ std::vector<int> shapeIds;
+ for (size_t i = 0; i < this->features.size(); i++) {
+ std::vector<int> newShapeIds = this->features[i]->BuildShapeIndex(index);
+ for (size_t j = 0; j < newShapeIds.size(); j++) {
+ shapeIds.push_back(newShapeIds[j]);
+ }
+ }
+ return shapeIds;
+ }
+
+ void Export(WKGeometryHandler* handler, uint32_t partId) {
+ WKGeometryMeta meta(WKGeometryType::GeometryCollection, false, false, false);
+ meta.hasSize = true;
+ meta.size = this->features.size();
+
+ handler->nextGeometryStart(meta, partId);
+ for (size_t i = 0; i < this->features.size(); i++) {
+ this->features[i]->Export(handler, i);
+ }
+ handler->nextGeometryEnd(meta, partId);
+ }
+
+ class Builder: public GeographyBuilder {
+ public:
+
+ Builder(bool oriented, bool check):
+ metaPtr(nullptr), builderPtr(nullptr), builderMetaPtr(nullptr),
+ oriented(oriented), check(check) {}
+
+ virtual void nextGeometryStart(const WKGeometryMeta& meta, uint32_t partId) {
+ // if this is the first call, store the meta reference associated with this geometry
+ if (this->metaPtr == nullptr) {
+ this->metaPtr = (WKGeometryMeta*) &meta;
+ return;
+ }
+
+ if (!this->builderPtr) {
+ // store a reference to the meta associated with this
+ // builder so that we know when the corresponding nextGeometryEnd()
+ // is called
+ this->builderMetaPtr = (WKGeometryMeta*) &meta;
+
+ switch (meta.geometryType) {
+ case WKGeometryType::Point:
+ case WKGeometryType::MultiPoint:
+ this->builderPtr = absl::make_unique<PointGeography::Builder>();
+ break;
+ case WKGeometryType::LineString:
+ case WKGeometryType::MultiLineString:
+ this->builderPtr = absl::make_unique<PolylineGeography::Builder>();
+ break;
+ case WKGeometryType::Polygon:
+ case WKGeometryType::MultiPolygon:
+ this->builderPtr = absl::make_unique<PolygonGeography::Builder>(
+ this->oriented,
+ this->check
+ );
+ break;
+ case WKGeometryType::GeometryCollection:
+ this->builderPtr = absl::make_unique<GeographyCollection::Builder>(
+ this->oriented,
+ this->check
+ );
+ break;
+ default:
+ std::stringstream err;
+ err << "Unknown geometry type in geography builder: " << meta.geometryType;
+ Rcpp::stop(err.str());
+ }
+ }
+
+ this->builder()->nextGeometryStart(meta, partId);
+ }
+
+ virtual void nextLinearRingStart(const WKGeometryMeta& meta, uint32_t size, uint32_t ringId) {
+ this->builder()->nextLinearRingStart(meta, size, ringId);
+ }
+
+ virtual void nextCoordinate(const WKGeometryMeta& meta, const WKCoord& coord, uint32_t coordId) {
+ this->builder()->nextCoordinate(meta, coord, coordId);
+ }
+
+ virtual void nextLinearRingEnd(const WKGeometryMeta& meta, uint32_t size, uint32_t ringId) {
+ this->builder()->nextLinearRingEnd(meta, size, ringId);
+ }
+
+ virtual void nextGeometryEnd(const WKGeometryMeta& meta, uint32_t partId) {
+ // the end of this GEOMETRYCOLLECTION
+ if (&meta == this->metaPtr) {
+ return;
+ }
+
+ this->builder()->nextGeometryEnd(meta, partId);
+
+ if (&meta == this->builderMetaPtr) {
+ std::unique_ptr<Geography> feature = this->builder()->build();
+ features.push_back(std::move(feature));
+ this->builderPtr = std::unique_ptr<GeographyBuilder>(nullptr);
+ this->builderMetaPtr = nullptr;
+ }
+ }
+
+ std::unique_ptr<Geography> build() {
+ return absl::make_unique<GeographyCollection>(std::move(this->features));
+ }
+
+ private:
+ std::vector<std::unique_ptr<Geography>> features;
+ WKGeometryMeta* metaPtr;
+ std::unique_ptr<GeographyBuilder> builderPtr;
+ WKGeometryMeta* builderMetaPtr;
+ bool oriented;
+ bool check;
+
+ GeographyBuilder* builder() {
+ if (this->builderPtr) {
+ return this->builderPtr.get();
+ } else {
+ Rcpp::stop("Invalid nesting in geometrycollection (can't find nested builder)");
+ }
+ }
+ };
+
+private:
+ std::vector<std::unique_ptr<Geography>> features;
+};
+
+#endif
--- /dev/null
+
+#ifndef GEOGRAPHY_OPERATOR_H
+#define GEOGRAPHY_OPERATOR_H
+
+#include "geography.h"
+#include <Rcpp.h>
+
+template<class VectorType, class ScalarType>
+class UnaryGeographyOperator {
+public:
+ VectorType processVector(Rcpp::List geog) {
+ VectorType output(geog.size());
+
+ SEXP item;
+ for (R_xlen_t i = 0; i < geog.size(); i++) {
+ Rcpp::checkUserInterrupt();
+
+ item = geog[i];
+ if (item == R_NilValue) {
+ output[i] = VectorType::get_na();
+ } else {
+ Rcpp::XPtr<Geography> feature(item);
+ output[i] = this->processFeature(feature, i);
+ }
+ }
+
+ return output;
+ }
+
+ virtual ScalarType processFeature(Rcpp::XPtr<Geography> feature, R_xlen_t i) = 0;
+};
+
+
+template<class VectorType, class ScalarType>
+class BinaryGeographyOperator {
+public:
+ VectorType processVector(Rcpp::List geog1, Rcpp::List geog2) {
+ if (geog2.size() != geog1.size()) {
+ Rcpp::stop("Incompatible lengths");
+ }
+
+ VectorType output(geog1.size());
+
+ SEXP item1;
+ SEXP item2;
+
+ for (R_xlen_t i = 0; i < geog1.size(); i++) {
+ Rcpp::checkUserInterrupt();
+
+ item1 = geog1[i];
+ item2 = geog2[i];
+ if (item1 == R_NilValue || item2 == R_NilValue) {
+ output[i] = VectorType::get_na();
+ } else {
+ Rcpp::XPtr<Geography> feature1(item1);
+ Rcpp::XPtr<Geography> feature2(item2);
+ output[i] = processFeature(feature1, feature2, i);
+ }
+ }
+
+ return output;
+ }
+
+ virtual ScalarType processFeature(Rcpp::XPtr<Geography> feature1,
+ Rcpp::XPtr<Geography> feature2,
+ R_xlen_t i) = 0;
+};
+
+#endif
--- /dev/null
+
+#ifndef GEOGRAPHY_H
+#define GEOGRAPHY_H
+
+#include <memory>
+#include "s2/s2latlng.h"
+#include "s2/s2polyline.h"
+#include "s2/s2polygon.h"
+#include "s2/s2shape_index.h"
+#include "s2/s2shape_index_region.h"
+#include "s2/mutable_s2shape_index.h"
+#include "s2/s2point_vector_shape.h"
+#include "s2/s2cap.h"
+#include "wk/geometry-handler.hpp"
+#include <Rcpp.h>
+
+class Geography {
+public:
+
+ Geography(): hasIndex(false) {}
+
+ // accessors need to be methods, since their calculation
+ // depends on the geometry type
+
+ // returns true for a multi-
+ // or geometrycollection type
+ virtual bool IsCollection() = 0;
+ // Returns 0 for point, 1 for line, 2 for polygon
+ virtual int Dimension() = 0;
+ // Returns the number of points in the input
+ virtual int NumPoints() = 0;
+ virtual bool IsEmpty() = 0;
+ virtual double Area() = 0;
+ virtual double Length() = 0;
+ virtual double Perimeter() = 0;
+ virtual double X() = 0;
+ virtual double Y() = 0;
+ virtual S2Point Centroid() = 0;
+ virtual std::unique_ptr<Geography> Boundary() = 0;
+
+ // every type will build the index differently based on
+ // the underlying data, and this can (should?) be done
+ // lazily. Returns a vector of shape IDs so the caller
+ // can keep track of which shape came from which feature.
+ virtual std::vector<int> BuildShapeIndex(MutableS2ShapeIndex* index) = 0;
+
+ // the factory handler is responsible for building these objects
+ // but exporting can be done here
+ virtual void Export(WKGeometryHandler* handler, uint32_t partId) = 0;
+
+ virtual ~Geography() {}
+
+ // other calculations use ShapeIndex
+ virtual S2ShapeIndex* ShapeIndex() {
+ if (!this->hasIndex) {
+ this->BuildShapeIndex(&this->shape_index_);
+ this->hasIndex = true;
+ }
+
+ return &this->shape_index_;
+ }
+
+ virtual S2ShapeIndexRegion<S2ShapeIndex> ShapeIndexRegion() {
+ S2ShapeIndex *ix = this->ShapeIndex();
+ return MakeS2ShapeIndexRegion(ix);
+ }
+
+ virtual S2Cap GetCapBound() {
+ return this->ShapeIndexRegion().GetCapBound();
+ }
+
+ virtual S2LatLngRect GetRectBound() {
+ return this->ShapeIndexRegion().GetRectBound();
+ }
+
+protected:
+ MutableS2ShapeIndex shape_index_;
+ bool hasIndex;
+};
+
+
+class GeographyBuilder: public WKGeometryHandler {
+public:
+ virtual std::unique_ptr<Geography> build() = 0;
+ virtual ~GeographyBuilder() {}
+};
+
+#endif
--- /dev/null
+
+#include "s2/s2debug.h"
+#include <Rcpp.h>
+using namespace Rcpp;
+
+// [[Rcpp::export]]
+void cpp_s2_init() {
+ // It's important to set this flag, as users might have "debug" flags
+ // for their build environment, and there are some checks that will terminate
+ // R instead of throw an exception if this value is set to true.
+ // When possible, we also disable debug checks on a per-operation basis
+ // if there is another way to do so (e.g., constructing S2Loop and S2Polygon objects).
+ FLAGS_s2debug = false; // # nocov
+}
--- /dev/null
+
+#ifndef POINT_GEOGRAPHY_H
+#define POINT_GEOGRAPHY_H
+
+#include "s2/s2latlng_rect.h"
+
+#include "geography.h"
+
+// This class handles both points and multipoints, as this is how
+// points are generally returned/required in S2 (vector of S2Point)
+// This is similar to an S2PointVectorLayer
+class PointGeography: public Geography {
+public:
+ PointGeography(): points(0) {}
+ PointGeography(S2Point point): points(1) {
+ this->points[0] = point;
+ }
+ PointGeography(std::vector<S2Point> points): points(points) {}
+
+ bool IsCollection() {
+ return this->points.size() > 1;
+ }
+
+ int Dimension() {
+ return 0;
+ }
+
+ int NumPoints() {
+ return this->points.size();
+ }
+
+ bool IsEmpty() {
+ return this->points.size() == 0;
+ }
+
+ double Area() {
+ return 0;
+ }
+
+ double Length() {
+ return 0;
+ }
+
+ double Perimeter() {
+ return 0;
+ }
+
+ double X() {
+ if (this->points.size() != 1) {
+ return NA_REAL;
+ } else {
+ S2LatLng latLng(this->points[0]);
+ return latLng.lng().degrees();
+ }
+ }
+
+ double Y() {
+ if (this->points.size() != 1) {
+ return NA_REAL;
+ } else {
+ S2LatLng latLng(this->points[0]);
+ return latLng.lat().degrees();
+ }
+ }
+
+ S2Point Centroid() {
+ S2Point output(0, 0, 0);
+ for (size_t i = 0; i < this->points.size(); i++) {
+ output += this->points[i];
+ }
+
+ return output;
+ }
+
+ S2LatLngRect GetRectBound() {
+ S2LatLngRect rect;
+ for (size_t i = 0; i < this->points.size(); i++) {
+ rect.AddPoint(this->points[i]); // depends on order
+ }
+ return rect;
+ }
+
+ std::unique_ptr<Geography> Boundary() {
+ return absl::make_unique<PointGeography>();
+ }
+
+ std::vector<int> BuildShapeIndex(MutableS2ShapeIndex* index) {
+ std::vector<int> shapeIds(1);
+ std::vector<S2Point> pointsCopy(this->points);
+
+ shapeIds[0] = index->Add(std::unique_ptr<S2PointVectorShape>(
+ new S2PointVectorShape(std::move(pointsCopy)))
+ );
+ return shapeIds;
+ }
+
+ void Export(WKGeometryHandler* handler, uint32_t partId) {
+ S2LatLng point;
+
+ if (this->points.size() > 1) {
+ // export multipoint
+ WKGeometryMeta meta(WKGeometryType::MultiPoint, false, false, false);
+ meta.hasSize = true;
+ meta.size = this->points.size();
+
+ WKGeometryMeta childMeta(WKGeometryType::Point, false, false, false);
+ childMeta.hasSize = true;
+ childMeta.size = 1;
+
+ handler->nextGeometryStart(meta, partId);
+
+ for (size_t i = 0; i < this->points.size(); i++) {
+ point = S2LatLng(this->points[i]);
+
+ handler->nextGeometryStart(childMeta, i);
+ handler->nextCoordinate(meta, WKCoord::xy(point.lng().degrees(), point.lat().degrees()), 0);
+ handler->nextGeometryEnd(childMeta, i);
+ }
+
+ handler->nextGeometryEnd(meta, partId);
+
+ } else if (this->points.size() > 0) {
+ // export point
+ WKGeometryMeta meta(WKGeometryType::Point, false, false, false);
+ meta.hasSize = true;
+ meta.size = this->points.size();
+
+ handler->nextGeometryStart(meta, partId);
+
+ point = S2LatLng(this->points[0]);
+ handler->nextCoordinate(meta, WKCoord::xy(point.lng().degrees(), point.lat().degrees()), 0);
+
+ handler->nextGeometryEnd(meta, partId);
+ } else {
+ // export empty point
+ // export point
+ WKGeometryMeta meta(WKGeometryType::Point, false, false, false);
+ meta.hasSize = true;
+ meta.size = 0;
+ handler->nextGeometryStart(meta, partId);
+ handler->nextGeometryEnd(meta, partId);
+ }
+ }
+
+ class Builder: public GeographyBuilder {
+ public:
+ void nextCoordinate(const WKGeometryMeta& meta, const WKCoord& coord, uint32_t coordId) {
+ points.push_back(S2LatLng::FromDegrees(coord.y, coord.x).Normalized().ToPoint());
+ }
+
+ std::unique_ptr<Geography> build() {
+ return absl::make_unique<PointGeography>(std::move(this->points));
+ }
+
+ private:
+ std::vector<S2Point> points;
+ };
+
+private:
+ std::vector<S2Point> points;
+};
+
+#endif
--- /dev/null
+
+#ifndef POLYGON_GEOGRAPHY_H
+#define POLYGON_GEOGRAPHY_H
+
+#include "wk/reader.hpp"
+
+#include "geography.h"
+#include "point-geography.h"
+#include "polyline-geography.h"
+
+// This class handles polygons (POLYGON and MULTIPOLYGON)
+// This is similar to an S2PolygonLayer
+class PolygonGeography: public Geography {
+public:
+ PolygonGeography() {}
+ PolygonGeography(std::unique_ptr<S2Polygon> polygon):
+ polygon(std::move(polygon)) {}
+
+ bool IsCollection() {
+ return this->outerLoopIndices().size() > 1;
+ }
+
+ int Dimension() {
+ return 2;
+ }
+
+ int NumPoints() {
+ return this->polygon->num_vertices();
+ }
+
+ bool IsEmpty() {
+ return this->polygon->is_empty();
+ }
+
+ double Area() {
+ return this->polygon->GetArea();
+ }
+
+ double Length() {
+ return 0;
+ }
+
+ double Perimeter() {
+ std::unique_ptr<Geography> boundary = this->Boundary();
+ return boundary->Length();
+ }
+
+ double X() {
+ Rcpp::stop("Can't compute X value of a non-point geography");
+ }
+
+ double Y() {
+ Rcpp::stop("Can't compute Y value of a non-point geography");
+ }
+
+ S2Point Centroid() {
+ return this->polygon->GetCentroid();
+ }
+
+ S2Cap GetCapBound() {
+ return this->polygon->GetCapBound();
+ }
+
+ S2LatLngRect GetRectBound() {
+ return this->polygon->GetRectBound();
+ }
+
+ std::unique_ptr<Geography> Boundary() {
+ PolylineGeography::Builder builder;
+ std::vector<std::vector<int>> flatIndices = this->flatLoopIndices();
+
+ // export multilinestring
+ WKGeometryMeta meta(WKGeometryType::MultiLineString, false, false, false);
+ meta.hasSize = true;
+ meta.size = this->polygon->num_loops();
+
+ builder.nextGeometryStart(meta, WKReader::PART_ID_NONE);
+ int loopId = 0;
+ for (int i = 0; i < flatIndices.size(); i++) {
+ this->exportLoops(&builder, meta, flatIndices[i], loopId);
+ loopId += flatIndices[i].size();
+ }
+ builder.nextGeometryEnd(meta, WKReader::PART_ID_NONE);
+
+ return builder.build();
+ }
+
+ std::vector<int> BuildShapeIndex(MutableS2ShapeIndex* index) {
+ std::vector<int> shapeIds(1);
+ std::unique_ptr<S2Polygon::Shape> shape = absl::make_unique<S2Polygon::Shape>();
+ shape->Init(this->polygon.get());
+ shapeIds[0] = index->Add(std::move(shape));
+ return shapeIds;
+ }
+
+ void Export(WKGeometryHandler* handler, uint32_t partId) {
+ std::vector<std::vector<int>> flatIndices = this->flatLoopIndices();
+
+ if (flatIndices.size() > 1) {
+ // export multipolygon
+ WKGeometryMeta meta(WKGeometryType::MultiPolygon, false, false, false);
+ meta.hasSize = true;
+ meta.size = flatIndices.size();
+
+ WKGeometryMeta childMeta(WKGeometryType::Polygon, false, false, false);
+ childMeta.hasSize = true;
+
+ handler->nextGeometryStart(meta, partId);
+ for (int i = 0; i < flatIndices.size(); i++) {
+ childMeta.size = flatIndices[i].size();
+ handler->nextGeometryStart(childMeta, i);
+ this->exportLoops(handler, childMeta, flatIndices[i]);
+ handler->nextGeometryEnd(childMeta, i);
+ }
+
+ handler->nextGeometryEnd(meta, partId);
+
+ } else if (flatIndices.size() > 0) {
+ // export polygon
+ WKGeometryMeta meta(WKGeometryType::Polygon, false, false, false);
+ meta.hasSize = true;
+ meta.size = flatIndices[0].size();
+ handler->nextGeometryStart(meta, partId);
+ this->exportLoops(handler, meta, flatIndices[0]);
+ handler->nextGeometryEnd(meta, partId);
+
+ } else {
+ // export empty polygon
+ WKGeometryMeta meta(WKGeometryType::Polygon, false, false, false);
+ meta.hasSize = true;
+ meta.size = 0;
+ handler->nextGeometryStart(meta, partId);
+ handler->nextGeometryEnd(meta, partId);
+ }
+ }
+
+ class Builder: public GeographyBuilder {
+ public:
+ Builder(bool oriented, bool check):
+ oriented(oriented), check(check) {}
+
+ void nextLinearRingStart(const WKGeometryMeta& meta, uint32_t size, uint32_t ringId) {
+ // skip the last vertex (WKB rings are theoretically closed)
+ if (size > 0) {
+ this->vertices = std::vector<S2Point>(size - 1);
+ } else {
+ this->vertices = std::vector<S2Point>();
+ }
+ }
+
+ void nextCoordinate(const WKGeometryMeta& meta, const WKCoord& coord, uint32_t coordId) {
+ if (coordId < this->vertices.size()) {
+ vertices[coordId] = S2LatLng::FromDegrees(coord.y, coord.x).Normalized().ToPoint();
+ }
+ }
+
+ void nextLinearRingEnd(const WKGeometryMeta& meta, uint32_t size, uint32_t ringId) {
+ std::unique_ptr<S2Loop> loop = absl::make_unique<S2Loop>();
+ loop->set_s2debug_override(S2Debug::DISABLE);
+ loop->Init(vertices);
+
+ if (!oriented) {
+ loop->Normalize();
+ }
+
+ if (this->check && !loop->IsValid()) {
+ std::stringstream err;
+ err << "Loop " << (this->loops.size()) << " is not valid: ";
+ S2Error error;
+ loop->FindValidationError(&error);
+ err << error.text();
+ Rcpp::stop(err.str());
+ }
+
+ this->loops.push_back(std::move(loop));
+ }
+
+ std::unique_ptr<Geography> build() {
+ std::unique_ptr<S2Polygon> polygon = absl::make_unique<S2Polygon>();
+ polygon->set_s2debug_override(S2Debug::DISABLE);
+ if (this->loops.size() > 0 && oriented) {
+ polygon->InitOriented(std::move(this->loops));
+ } else if (this->loops.size() > 0) {
+ polygon->InitNested(std::move(this->loops));
+ }
+
+ // make sure polygon is valid
+ if (this->check && !polygon->IsValid()) {
+ S2Error error;
+ polygon->FindValidationError(&error);
+ Rcpp::stop(error.text());
+ }
+
+ return absl::make_unique<PolygonGeography>(std::move(polygon));
+ }
+
+ private:
+ bool oriented;
+ bool check;
+ std::vector<S2Point> vertices;
+ std::vector<std::unique_ptr<S2Loop>> loops;
+ };
+
+private:
+ std::unique_ptr<S2Polygon> polygon;
+
+ // Calculate which loops in the polygon are outer loops (loop->depth() == 0)
+ std::vector<int> outerLoopIndices() {
+ std::vector<int> indices;
+ for (int i = 0; i < this->polygon->num_loops(); i++) {
+ if (this->polygon->loop(i)->depth() == 0) {
+ indices.push_back(i);
+ }
+ }
+
+ return indices;
+ }
+
+ // Calculate the arrangement of loops in the form of a multipolygon
+ // (list(list(shell, !!! holes)))
+ std::vector<std::vector<int>> flatLoopIndices() {
+ std::vector<int> outerLoops = this->outerLoopIndices();
+
+ std::vector<std::vector<int>> flatIndices(outerLoops.size());
+ for (int i = 0; i < outerLoops.size(); i++) {
+ int k = outerLoops[i];
+ flatIndices[i] = std::vector<int>();
+
+ // the first loop here is the shell (depth == 0)
+ flatIndices[i].push_back(k);
+
+ // loops in the S2Polygon are arranged such that child loops are
+ // directly after the outer loop, so add all loop indices before
+ // the next parent loop (or end of polygon). This is similar to
+ // S2Polygon::GetLastDescendant() but is slightly easier to understand.
+ while (++k < this->polygon->num_loops() && this->polygon->loop(k)->depth() > 0) {
+ flatIndices[i].push_back(k);
+ }
+ }
+
+ return flatIndices;
+ }
+
+ void exportLoops(WKGeometryHandler* handler, WKGeometryMeta meta,
+ const std::vector<int>& loopIndices, int loopIdOffset = 0) {
+ S2LatLng point;
+
+ for (size_t i = 0; i < loopIndices.size(); i++) {
+ int loopId = loopIndices[i];
+ S2Loop* loop = this->polygon->loop(loopId);
+ if (loop->num_vertices() == 0) {
+ continue;
+ }
+
+ // this is a slightly ugly way to make it possible to export either the
+ // boundaries or the loops using the same code
+ WKGeometryMeta childMeta(WKGeometryType::LineString, false, false, false);
+ childMeta.hasSize = true;
+ childMeta.size = loop->num_vertices() + 1;
+
+ WKGeometryMeta coordMeta;
+
+ if (meta.geometryType == WKGeometryType::Polygon) {
+ handler->nextLinearRingStart(meta, loop->num_vertices() + 1, i + loopIdOffset);
+ coordMeta = meta;
+ } else if (meta.geometryType == WKGeometryType::MultiLineString) {
+ handler->nextGeometryStart(childMeta, i + loopIdOffset);
+ coordMeta = childMeta;
+ } else {
+ std::stringstream err;
+ err << "Can't export S2Loop with parent geometry type " << meta.geometryType;
+ Rcpp::stop(err.str());
+ }
+
+ if ((loop->depth() % 2) == 0) {
+ // if this is the first ring, use the internal vertex order
+ for (int j = 0; j < loop->num_vertices(); j++) {
+ point = S2LatLng(loop->vertex(j));
+ handler->nextCoordinate(
+ coordMeta,
+ WKCoord::xy(point.lng().degrees(), point.lat().degrees()),
+ j
+ );
+ }
+
+ // close the loop!
+ point = S2LatLng(loop->vertex(0));
+ handler->nextCoordinate(
+ coordMeta,
+ WKCoord::xy(point.lng().degrees(), point.lat().degrees()),
+ loop->num_vertices()
+ );
+ } else {
+ // if an interior ring, reverse the vertex order
+ for (int j = 0; j < loop->num_vertices(); j++) {
+ point = S2LatLng(loop->vertex(loop->num_vertices() - 1 - j));
+ handler->nextCoordinate(
+ coordMeta,
+ WKCoord::xy(point.lng().degrees(), point.lat().degrees()),
+ j
+ );
+ }
+
+ // close the loop!
+ point = S2LatLng(loop->vertex(loop->num_vertices() - 1));
+ handler->nextCoordinate(
+ coordMeta,
+ WKCoord::xy(point.lng().degrees(), point.lat().degrees()),
+ loop->num_vertices()
+ );
+ }
+
+ if (meta.geometryType == WKGeometryType::Polygon) {
+ handler->nextLinearRingEnd(meta, loop->num_vertices() + 1, i + loopIdOffset);
+ } else if (meta.geometryType == WKGeometryType::MultiLineString) {
+ handler->nextGeometryEnd(childMeta, i + loopIdOffset);
+ }
+ }
+ }
+};
+
+#endif
--- /dev/null
+
+#ifndef POLYLINE_GEOGRAPHY_H
+#define POLYLINE_GEOGRAPHY_H
+
+#include "s2/s2latlng_rect.h"
+
+#include "geography.h"
+
+// This class handles (vectors of) polylines (LINESTRING and MULTILINESTRING)
+// This is similar to an S2PolylineVectorLayer
+class PolylineGeography: public Geography {
+public:
+ PolylineGeography(): polylines(0) {}
+ PolylineGeography(std::vector<std::unique_ptr<S2Polyline>> polylines):
+ polylines(std::move(polylines)) {}
+
+ bool IsCollection() {
+ return this->polylines.size() > 1;
+ }
+
+ int Dimension() {
+ return 1;
+ }
+
+ int NumPoints() {
+ int numPoints = 0;
+ for (size_t i = 0; i < this->polylines.size(); i++) {
+ numPoints += this->polylines[i]->num_vertices();
+ }
+
+ return numPoints;
+ }
+
+ bool IsEmpty() {
+ for (size_t i = 0; i < this->polylines.size(); i++) {
+ if (this->polylines[i]->num_vertices() > 0) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ double Area() {
+ return 0;
+ }
+
+ double Length() {
+ double length = 0;
+ for (size_t i = 0; i < this->polylines.size(); i++) {
+ length += this->polylines[i]->GetLength().radians();
+ }
+
+ return length;
+ }
+
+ double Perimeter() {
+ return 0;
+ }
+
+ double X() {
+ Rcpp::stop("Can't compute X value of a non-point geography");
+ }
+
+ double Y() {
+ Rcpp::stop("Can't compute Y value of a non-point geography");
+ }
+
+ S2Point Centroid() {
+ S2Point output(0, 0, 0);
+ for (size_t i = 0; i < this->polylines.size(); i++) {
+ output += this->polylines[i]->GetCentroid();
+ }
+
+ return output;
+ }
+
+ S2LatLngRect GetRectBound() {
+ S2LatLngRect rect;
+ if (this->polylines.size())
+ rect = this->polylines[0]->GetRectBound();
+ for (size_t i = 1; i < this->polylines.size(); i++) {
+ rect.Union(this->polylines[i]->GetRectBound()); // depends on order
+ }
+ return rect;
+ }
+
+ std::unique_ptr<Geography> Boundary() {
+ std::vector<S2Point> endpoints;
+ for (size_t i = 0; i < this->polylines.size(); i++) {
+ if (this->polylines[i]->num_vertices() >= 2) {
+ endpoints.push_back(this->polylines[i]->vertex(0));
+ endpoints.push_back(this->polylines[i]->vertex(1));
+ }
+ }
+
+ return absl::make_unique<PointGeography>(endpoints);
+ }
+
+ std::vector<int> BuildShapeIndex(MutableS2ShapeIndex* index) {
+ std::vector<int> shapeIds(this->polylines.size());
+ for (size_t i = 0; i < this->polylines.size(); i++) {
+ std::unique_ptr<S2Polyline::Shape> shape = absl::make_unique<S2Polyline::Shape>();
+ shape->Init(this->polylines[i].get());
+ shapeIds[i] = index->Add(std::move(shape));
+ }
+ return shapeIds;
+ }
+
+ void Export(WKGeometryHandler* handler, uint32_t partId) {
+ S2LatLng point;
+
+ if (this->polylines.size() > 1) {
+ // export multilinestring
+ WKGeometryMeta meta(WKGeometryType::MultiLineString, false, false, false);
+ meta.hasSize = true;
+ meta.size = this->polylines.size();
+
+ handler->nextGeometryStart(meta, partId);
+
+ for (size_t i = 0; i < this->polylines.size(); i++) {
+ WKGeometryMeta childMeta(WKGeometryType::LineString, false, false, false);
+ childMeta.hasSize = true;
+ childMeta.size = this->polylines[i]->num_vertices();
+
+ handler->nextGeometryStart(childMeta, i);
+
+ for (size_t j = 0; j < childMeta.size; j++) {
+ point = S2LatLng(this->polylines[i]->vertex(j));
+ handler->nextCoordinate(meta, WKCoord::xy(point.lng().degrees(), point.lat().degrees()), j);
+ }
+
+ handler->nextGeometryEnd(childMeta, i);
+ }
+
+ handler->nextGeometryEnd(meta, partId);
+
+ } else if (this->polylines.size() > 0) {
+ // export linestring
+ WKGeometryMeta meta(WKGeometryType::LineString, false, false, false);
+ meta.hasSize = true;
+ meta.size = this->polylines[0]->num_vertices();
+
+ handler->nextGeometryStart(meta, partId);
+
+ for (size_t i = 0; i < meta.size; i++) {
+ point = S2LatLng(this->polylines[0]->vertex(i));
+ handler->nextCoordinate(meta, WKCoord::xy(point.lng().degrees(), point.lat().degrees()), i);
+ }
+
+ handler->nextGeometryEnd(meta, partId);
+
+ } else {
+ // export empty linestring
+ WKGeometryMeta meta(WKGeometryType::LineString, false, false, false);
+ meta.hasSize = true;
+ meta.size = this->polylines[0]->num_vertices();
+ handler->nextGeometryStart(meta, partId);
+ handler->nextGeometryEnd(meta, partId);
+ }
+ }
+
+ class Builder: public GeographyBuilder {
+ public:
+ void nextGeometryStart(const WKGeometryMeta& meta, uint32_t partId) {
+ if (meta.geometryType == WKGeometryType::LineString) {
+ points = std::vector<S2Point>(meta.size);
+ }
+ }
+
+ void nextCoordinate(const WKGeometryMeta& meta, const WKCoord& coord, uint32_t coordId) {
+ points[coordId] = S2LatLng::FromDegrees(coord.y, coord.x).Normalized().ToPoint();
+ }
+
+ void nextGeometryEnd(const WKGeometryMeta& meta, uint32_t partId) {
+ if (meta.geometryType == WKGeometryType::LineString) {
+ polylines.push_back(absl::make_unique<S2Polyline>(std::move(points)));
+ }
+ }
+
+ std::unique_ptr<Geography> build() {
+ return absl::make_unique<PolylineGeography>(std::move(this->polylines));
+ }
+
+ private:
+ std::vector<S2Point> points;
+ std::vector<std::unique_ptr<S2Polyline>> polylines;
+ };
+
+private:
+ std::vector<std::unique_ptr<S2Polyline>> polylines;
+};
+
+#endif
--- /dev/null
+
+#include "geography-operator.h"
+#include "s2/s2closest_edge_query.h"
+#include "s2/s2furthest_edge_query.h"
+#include <Rcpp.h>
+using namespace Rcpp;
+
+// [[Rcpp::export]]
+LogicalVector cpp_s2_is_collection(List geog) {
+ class Op: public UnaryGeographyOperator<LogicalVector, int> {
+ int processFeature(XPtr<Geography> feature, R_xlen_t i) {
+ return feature->IsCollection();
+ }
+ };
+
+ Op op;
+ return op.processVector(geog);
+}
+
+// [[Rcpp::export]]
+IntegerVector cpp_s2_dimension(List geog) {
+ class Op: public UnaryGeographyOperator<IntegerVector, int> {
+ int processFeature(XPtr<Geography> feature, R_xlen_t i) {
+ return feature->Dimension();
+ }
+ };
+
+ Op op;
+ return op.processVector(geog);
+}
+
+// [[Rcpp::export]]
+IntegerVector cpp_s2_num_points(List geog) {
+ class Op: public UnaryGeographyOperator<IntegerVector, int> {
+ int processFeature(XPtr<Geography> feature, R_xlen_t i) {
+ return feature->NumPoints();
+ }
+ };
+
+ Op op;
+ return op.processVector(geog);
+}
+
+// [[Rcpp::export]]
+LogicalVector cpp_s2_is_empty(List geog) {
+ class Op: public UnaryGeographyOperator<LogicalVector, int> {
+ int processFeature(XPtr<Geography> feature, R_xlen_t i) {
+ return feature->IsEmpty();
+ }
+ };
+
+ Op op;
+ return op.processVector(geog);
+}
+
+// [[Rcpp::export]]
+NumericVector cpp_s2_area(List geog) {
+ class Op: public UnaryGeographyOperator<NumericVector, double> {
+ double processFeature(XPtr<Geography> feature, R_xlen_t i) {
+ return feature->Area();
+ }
+ };
+
+ Op op;
+ return op.processVector(geog);
+}
+
+// [[Rcpp::export]]
+NumericVector cpp_s2_length(List geog) {
+ class Op: public UnaryGeographyOperator<NumericVector, double> {
+ double processFeature(XPtr<Geography> feature, R_xlen_t i) {
+ return feature->Length();
+ }
+ };
+
+ Op op;
+ return op.processVector(geog);
+}
+
+// [[Rcpp::export]]
+NumericVector cpp_s2_perimeter(List geog) {
+ class Op: public UnaryGeographyOperator<NumericVector, double> {
+ double processFeature(XPtr<Geography> feature, R_xlen_t i) {
+ return feature->Perimeter();
+ }
+ };
+
+ Op op;
+ return op.processVector(geog);
+}
+
+// [[Rcpp::export]]
+NumericVector cpp_s2_x(List geog) {
+ class Op: public UnaryGeographyOperator<NumericVector, double> {
+ double processFeature(XPtr<Geography> feature, R_xlen_t i) {
+ return feature->X();
+ }
+ };
+
+ Op op;
+ return op.processVector(geog);
+}
+
+// [[Rcpp::export]]
+NumericVector cpp_s2_y(List geog) {
+ class Op: public UnaryGeographyOperator<NumericVector, double> {
+ double processFeature(XPtr<Geography> feature, R_xlen_t i) {
+ return feature->Y();
+ }
+ };
+
+ Op op;
+ return op.processVector(geog);
+}
+
+// [[Rcpp::export]]
+NumericVector cpp_s2_distance(List geog1, List geog2) {
+ class Op: public BinaryGeographyOperator<NumericVector, double> {
+
+ double processFeature(XPtr<Geography> feature1,
+ XPtr<Geography> feature2,
+ R_xlen_t i) {
+ S2ClosestEdgeQuery query(feature1->ShapeIndex());
+ S2ClosestEdgeQuery::ShapeIndexTarget target(feature2->ShapeIndex());
+
+ const auto& result = query.FindClosestEdge(&target);
+
+ S1ChordAngle angle = result.distance();
+ double distance = angle.ToAngle().radians();
+
+ if (distance == R_PosInf) {
+ return NA_REAL;
+ } else {
+ return distance;
+ }
+ }
+ };
+
+ Op op;
+ return op.processVector(geog1, geog2);
+}
+
+// [[Rcpp::export]]
+NumericVector cpp_s2_max_distance(List geog1, List geog2) {
+ class Op: public BinaryGeographyOperator<NumericVector, double> {
+
+ double processFeature(XPtr<Geography> feature1,
+ XPtr<Geography> feature2,
+ R_xlen_t i) {
+ S2FurthestEdgeQuery query(feature1->ShapeIndex());
+ S2FurthestEdgeQuery::ShapeIndexTarget target(feature2->ShapeIndex());
+
+ const auto& result = query.FindFurthestEdge(&target);
+
+ S1ChordAngle angle = result.distance();
+ double distance = angle.ToAngle().radians();
+
+ // returns -1 if one of the indexes is empty
+ // NA is more consistent with the BigQuery
+ // function, and makes way more sense
+ if (distance < 0) {
+ return NA_REAL;
+ } else {
+ return distance;
+ }
+ }
+ };
+
+ Op op;
+ return op.processVector(geog1, geog2);
+}
--- /dev/null
+
+#include "s2/s2latlng_rect.h"
+#include "s2/s2cap.h"
+
+#include "s2-options.h"
+#include "geography-operator.h"
+#include "point-geography.h"
+#include "polyline-geography.h"
+#include "polygon-geography.h"
+#include "geography-collection.h"
+
+#include <Rcpp.h>
+using namespace Rcpp;
+
+// [[Rcpp::export]]
+DataFrame cpp_s2_bounds_cap(List geog) {
+ SEXP item;
+ NumericVector lng(geog.size()), lat(geog.size()), angle(geog.size());
+
+ for (R_xlen_t i = 0; i < geog.size(); i++) {
+ Rcpp::checkUserInterrupt();
+ item = geog[i];
+ if (item == R_NilValue) {
+ lat[i] = lng[i] = angle[i] = NA_REAL;
+ } else {
+ Rcpp::XPtr<Geography> feature(item);
+ S2Cap cap = feature->GetCapBound();
+ S2LatLng center(cap.center());
+ lng[i] = center.lng().degrees();
+ lat[i] = center.lat().degrees();
+ angle[i] = cap.GetRadius().degrees();
+ }
+ }
+
+ return DataFrame::create(
+ _["lng"] = lng,
+ _["lat"] = lat,
+ _["angle"] = angle
+ );
+}
+
+// [[Rcpp::export]]
+DataFrame cpp_s2_bounds_rect(List geog) {
+ SEXP item;
+ NumericVector lng_lo(geog.size()), lat_lo(geog.size()), lng_hi(geog.size()), lat_hi(geog.size());
+
+ for (R_xlen_t i = 0; i < geog.size(); i++) {
+ Rcpp::checkUserInterrupt();
+ item = geog[i];
+ if (item == R_NilValue) {
+ lng_lo[i] = lat_lo[i] = lng_hi[i] = lat_hi[i] = NA_REAL;
+ } else {
+ Rcpp::XPtr<Geography> feature(item);
+ S2LatLngRect rect = feature->GetRectBound();
+ lng_lo[i] = rect.lng_lo().degrees();
+ lat_lo[i] = rect.lat_lo().degrees();
+ lng_hi[i] = rect.lng_hi().degrees();
+ lat_hi[i] = rect.lat_hi().degrees();
+ }
+ }
+
+ return DataFrame::create(
+ _["lng_lo"] = lng_lo,
+ _["lat_lo"] = lat_lo,
+ _["lng_hi"] = lng_hi,
+ _["lat_hi"] = lat_hi
+ );
+}
--- /dev/null
+
+#include <Rcpp.h>
+#include "wk/rcpp-translate.hpp"
+#include "wk/rcpp-coord-reader.hpp"
+
+#include "wk-geography.h"
+
+using namespace Rcpp;
+
+// [[Rcpp::export]]
+List cpp_s2_geog_point(NumericVector x, NumericVector y) {
+ NumericVector z(x.size());
+ z.fill(NA_REAL);
+ NumericVector m(x.size());
+ m.fill(NA_REAL);
+
+ WKRcppPointCoordProvider provider(x, y, z, m);
+ WKRcppPointCoordReader reader(provider);
+
+ WKGeographyWriter writer(provider.nFeatures());
+ reader.setHandler(&writer);
+
+ while (reader.hasNextFeature()) {
+ checkUserInterrupt();
+ reader.iterateFeature();
+ }
+
+ return writer.output;
+}
+
+// [[Rcpp::export]]
+List cpp_s2_make_line(NumericVector x, NumericVector y, IntegerVector featureId) {
+ NumericVector z(x.size());
+ z.fill(NA_REAL);
+ NumericVector m(x.size());
+ m.fill(NA_REAL);
+
+ WKRcppLinestringCoordProvider provider(x, y, z, m, featureId);
+ WKRcppLinestringCoordReader reader(provider);
+
+ WKGeographyWriter writer(provider.nFeatures());
+ reader.setHandler(&writer);
+
+ while (reader.hasNextFeature()) {
+ checkUserInterrupt();
+ reader.iterateFeature();
+ }
+
+ return writer.output;
+}
+
+// [[Rcpp::export]]
+List cpp_s2_make_polygon(NumericVector x, NumericVector y,
+ IntegerVector featureId, IntegerVector ringId,
+ bool oriented, bool check) {
+ NumericVector z(x.size());
+ z.fill(NA_REAL);
+ NumericVector m(x.size());
+ m.fill(NA_REAL);
+
+ WKRcppPolygonCoordProvider provider(x, y, z, m, featureId, ringId);
+ WKRcppPolygonCoordReader reader(provider);
+
+ WKGeographyWriter writer(provider.nFeatures());
+ writer.setOriented(oriented);
+ writer.setCheck(check);
+
+ reader.setHandler(&writer);
+
+ while (reader.hasNextFeature()) {
+ checkUserInterrupt();
+ reader.iterateFeature();
+ }
+
+ return writer.output;
+}
--- /dev/null
+
+#include "s2/s2latlng.h"
+#include "s2/s2polyline.h"
+#include "s2/s2polygon.h"
+
+#include "wk/wkb-reader.hpp"
+#include "wk/wkt-reader.hpp"
+#include "wk/wkb-writer.hpp"
+#include "wk/wkt-writer.hpp"
+#include "wk/geometry-formatter.hpp"
+
+#include "geography.h"
+#include "wk-geography.h"
+#include "point-geography.h"
+#include "polyline-geography.h"
+#include "polygon-geography.h"
+#include "geography-collection.h"
+
+#include <Rcpp.h>
+using namespace Rcpp;
+
+
+// [[Rcpp::export]]
+List s2_geography_from_wkb(List wkb, bool oriented, bool check) {
+ WKRawVectorListProvider provider(wkb);
+ WKGeographyWriter writer(wkb.size());
+ writer.setOriented(oriented);
+ writer.setCheck(check);
+
+ WKBReader reader(provider);
+ reader.setHandler(&writer);
+
+ while (reader.hasNextFeature()) {
+ checkUserInterrupt();
+ reader.iterateFeature();
+ }
+
+ return writer.output;
+}
+
+// [[Rcpp::export]]
+List s2_geography_from_wkt(CharacterVector wkt, bool oriented, bool check) {
+ WKCharacterVectorProvider provider(wkt);
+ WKGeographyWriter writer(wkt.size());
+ writer.setOriented(oriented);
+ writer.setCheck(check);
+
+ WKTReader reader(provider);
+ reader.setHandler(&writer);
+
+ while (reader.hasNextFeature()) {
+ checkUserInterrupt();
+ reader.iterateFeature();
+ }
+
+ return writer.output;
+}
+
+// [[Rcpp::export]]
+List s2_geography_full(LogicalVector x) { // create single geography with full polygon
+ std::unique_ptr<S2Loop> l = absl::make_unique<S2Loop>(S2Loop::kFull());
+ std::unique_ptr<S2Polygon> p = absl::make_unique<S2Polygon>(std::move(l));
+ Geography *pg = new PolygonGeography(std::move(p));
+ List ret(1);
+ ret(0) = Rcpp::XPtr<Geography>(pg);
+ return ret;
+}
+
+// [[Rcpp::export]]
+CharacterVector s2_geography_to_wkt(List s2_geography, int precision, bool trim) {
+ WKRcppSEXPProvider provider(s2_geography);
+ WKGeographyReader reader(provider);
+
+ WKCharacterVectorExporter exporter(reader.nFeatures());
+ exporter.setRoundingPrecision(precision);
+ exporter.setTrim(trim);
+ WKTWriter writer(exporter);
+
+ reader.setHandler(&writer);
+ while (reader.hasNextFeature()) {
+ checkUserInterrupt();
+ reader.iterateFeature();
+ }
+
+ return exporter.output;
+}
+
+// [[Rcpp::export]]
+List s2_geography_to_wkb(List s2_geography, int endian) {
+ WKRcppSEXPProvider provider(s2_geography);
+ WKGeographyReader reader(provider);
+
+ WKRawVectorListExporter exporter(reader.nFeatures());
+ WKBWriter writer(exporter);
+ writer.setEndian(endian);
+
+ reader.setHandler(&writer);
+ while (reader.hasNextFeature()) {
+ checkUserInterrupt();
+ reader.iterateFeature();
+ }
+
+ return exporter.output;
+}
+
+// [[Rcpp::export]]
+CharacterVector s2_geography_format(List s2_geography, int maxCoords, int precision, bool trim) {
+ WKRcppSEXPProvider provider(s2_geography);
+ WKGeographyReader reader(provider);
+
+ WKCharacterVectorExporter exporter(s2_geography.size());
+ exporter.setRoundingPrecision(precision);
+ exporter.setTrim(trim);
+ WKGeometryFormatter formatter(exporter, maxCoords);
+
+ reader.setHandler(&formatter);
+ while (reader.hasNextFeature()) {
+ checkUserInterrupt();
+ reader.iterateFeature();
+ }
+
+ return exporter.output;
+}
--- /dev/null
+
+#include "s2/s2latlng.h"
+#include "s2/s2point.h"
+#include "wk/rcpp-io.hpp"
+#include "wk/wkb-reader.hpp"
+#include "wk/wkb-writer.hpp"
+
+#include <Rcpp.h>
+using namespace Rcpp;
+
+// [[Rcpp::export]]
+List s2_lnglat_from_numeric(NumericVector lng, NumericVector lat) {
+ List output(lat.size());
+
+ S2LatLng item;
+ for (R_xlen_t i = 0; i < lat.size(); i++) {
+ item = S2LatLng::FromDegrees(lat[i], lng[i]);
+ output[i] = XPtr<S2LatLng>(new S2LatLng(item));
+ }
+
+ return output;
+}
+
+// [[Rcpp::export]]
+List s2_lnglat_from_s2_point(List s2_point) {
+ List output(s2_point.size());
+
+ SEXP item;
+ S2LatLng newItem;
+ for (R_xlen_t i = 0; i < s2_point.size(); i++) {
+ item = s2_point[i];
+ if (item == R_NilValue) {
+ output[i] = R_NilValue;
+ } else {
+ XPtr<S2Point> ptr(item);
+ output[i] = XPtr<S2LatLng>(new S2LatLng(*ptr));
+ }
+ }
+
+ return output;
+}
+
+// [[Rcpp::export]]
+List data_frame_from_s2_lnglat(List xptr) {
+ NumericVector lng(xptr.size());
+ NumericVector lat(xptr.size());
+
+ SEXP item;
+ for (R_xlen_t i = 0; i < xptr.size(); i++) {
+ item = xptr[i];
+ if (item == R_NilValue) {
+ lng[i] = NA_REAL;
+ lat[i] = NA_REAL;
+ } else {
+ XPtr<S2LatLng> ptr(item);
+ lng[i] = ptr->lng().degrees();
+ lat[i] = ptr->lat().degrees();
+ }
+ }
+
+ return List::create(_["lng"] = lng, _["lat"] = lat);
+}
--- /dev/null
+
+#include <unordered_map>
+#include <unordered_set>
+#include <algorithm>
+
+#include "s2/s2boolean_operation.h"
+#include "s2/s2closest_edge_query.h"
+#include "s2/s2furthest_edge_query.h"
+#include "s2/s2shape_index_region.h"
+
+#include "geography-operator.h"
+#include "s2-options.h"
+
+#include <Rcpp.h>
+using namespace Rcpp;
+
+std::unordered_map<int, R_xlen_t> buildSourcedIndex(List geog, MutableS2ShapeIndex* index) {
+ std::unordered_map<int, R_xlen_t> indexSource;
+ std::vector<int> shapeIds;
+
+ for (R_xlen_t j = 0; j < geog.size(); j++) {
+ checkUserInterrupt();
+ SEXP item2 = geog[j];
+
+ // build index and store index IDs so that shapeIds can be
+ // mapped back to the geog index
+ if (item2 == R_NilValue) {
+ Rcpp::stop("Missing `y` not allowed in binary indexed operators()");
+ } else {
+ Rcpp::XPtr<Geography> feature2(item2);
+ shapeIds = feature2->BuildShapeIndex(index);
+ for (size_t k = 0; k < shapeIds.size(); k ++) {
+ indexSource[shapeIds[k]] = j;
+ }
+ }
+ }
+
+ return indexSource;
+}
+
+std::unordered_set<R_xlen_t> findPossibleIntersections(const S2Region& region,
+ const MutableS2ShapeIndex* index,
+ std::unordered_map<int, R_xlen_t>& source,
+ int maxRegionCells) {
+
+ std::unordered_set<R_xlen_t> mightIntersectIndices;
+ MutableS2ShapeIndex::Iterator indexIterator(index);
+
+ // generate a small covering of the region
+ S2RegionCoverer coverer;
+ coverer.mutable_options()->set_max_cells(maxRegionCells);
+ S2CellUnion covering = coverer.GetCovering(region);
+
+ // iterate over cells in the featureIndex
+ for (S2CellId featureCellId: covering) {
+ S2ShapeIndex::CellRelation relation = indexIterator.Locate(featureCellId);
+
+ if (relation == S2ShapeIndex::CellRelation::INDEXED) {
+ // we're in luck! these indexes have this cell in common
+ // add all the features it contains as possible intersectors for featureIndex
+ const S2ShapeIndexCell& cell = indexIterator.cell();
+ for (int k = 0; k < cell.num_clipped(); k++) {
+ int shapeId = cell.clipped(k).shape_id();
+ mightIntersectIndices.insert(source[shapeId]);
+ }
+
+ } else if(relation == S2ShapeIndex::CellRelation::SUBDIVIDED) {
+ // promising! the geog2 index has a child cell of it.id()
+ // (at which indexIterator is now positioned)
+ // keep iterating until the iterator is done OR we're no longer at a child cell of
+ // it.id(). The ordering of the iterator isn't guaranteed anywhere in the documentation;
+ // however, this ordering would be consistent with that of a Normalized
+ // S2CellUnion.
+ while (!indexIterator.done() && featureCellId.contains(indexIterator.id())) {
+ // potentially many cells in the indexIterator, so let the user cancel if this is
+ // running too long
+ checkUserInterrupt();
+
+ // add all the features the child cell contains as possible intersectors for featureIndex
+ const S2ShapeIndexCell& cell = indexIterator.cell();
+ for (int k = 0; k < cell.num_clipped(); k++) {
+ int shapeId = cell.clipped(k).shape_id();
+ mightIntersectIndices.insert(source[shapeId]);
+ }
+
+ // go to the next cell in the index
+ indexIterator.Next();
+ }
+ }
+
+ // else: relation == S2ShapeIndex::CellRelation::DISJOINT (do nothing)
+ }
+
+ return mightIntersectIndices;
+}
+
+template<class VectorType, class ScalarType>
+class IndexedBinaryGeographyOperator: public UnaryGeographyOperator<VectorType, ScalarType> {
+public:
+ std::unique_ptr<MutableS2ShapeIndex> geog2Index;
+ std::unordered_map<int, R_xlen_t> geog2IndexSource;
+
+ IndexedBinaryGeographyOperator() {
+ this->geog2Index = absl::make_unique<MutableS2ShapeIndex>();
+ }
+
+ // maxEdgesPerCell should be between 10 and 50, with lower numbers
+ // leading to more memory usage (but potentially faster query times). Benchmarking
+ // with binary prediates seems to indicate that values on the high end
+ // of the spectrum do a reasonable job of efficient preselection, and that
+ // decreasing this value does little to increase performance.
+ virtual void buildIndex(List geog2, int maxEdgesPerCell = 50) {
+ MutableS2ShapeIndex::Options indexOptions;
+ indexOptions.set_max_edges_per_cell(maxEdgesPerCell);
+ this->geog2Index = absl::make_unique<MutableS2ShapeIndex>(indexOptions);
+ this->geog2IndexSource = buildSourcedIndex(geog2, this->geog2Index.get());
+ }
+};
+
+// -------- closest/farthest feature ----------
+
+// [[Rcpp::export]]
+IntegerVector cpp_s2_closest_feature(List geog1, List geog2) {
+
+ class Op: public IndexedBinaryGeographyOperator<IntegerVector, int> {
+ public:
+ int processFeature(Rcpp::XPtr<Geography> feature, R_xlen_t i) {
+ S2ClosestEdgeQuery query(this->geog2Index.get());
+ S2ClosestEdgeQuery::ShapeIndexTarget target(feature->ShapeIndex());
+ const auto& result = query.FindClosestEdge(&target);
+ if (result.is_empty()) {
+ return NA_INTEGER;
+ } else {
+ // convert to R index (+1)
+ return this->geog2IndexSource[result.shape_id()] + 1;
+ }
+ }
+ };
+
+ Op op;
+ op.buildIndex(geog2);
+ return op.processVector(geog1);
+}
+
+// [[Rcpp::export]]
+IntegerVector cpp_s2_farthest_feature(List geog1, List geog2) {
+
+ class Op: public IndexedBinaryGeographyOperator<IntegerVector, int> {
+ public:
+ int processFeature(Rcpp::XPtr<Geography> feature, R_xlen_t i) {
+ S2FurthestEdgeQuery query(this->geog2Index.get());
+ S2FurthestEdgeQuery::ShapeIndexTarget target(feature->ShapeIndex());
+ const auto& result = query.FindFurthestEdge(&target);
+ if (result.is_empty()) {
+ return NA_INTEGER;
+ } else {
+ // convert to R index (+1)
+ return this->geog2IndexSource[result.shape_id()] + 1;
+ }
+ }
+ };
+
+ Op op;
+ op.buildIndex(geog2);
+ return op.processVector(geog1);
+}
+
+// ----------- indexed binary predicate operators -----------
+
+class IndexedMatrixPredicateOperator: public IndexedBinaryGeographyOperator<List, IntegerVector> {
+public:
+ // a max_cells value of 8 was suggested in the S2RegionCoverer docs as a
+ // reasonable approximation of a geometry, although benchmarking seems to indicate that
+ // increasing this number above 4 actually decreasses performance (using a value
+ // of 1 dramatically decreases performance)
+ IndexedMatrixPredicateOperator(List s2options, int maxFeatureCells = 4):
+ maxFeatureCells(maxFeatureCells) {
+ GeographyOperationOptions options(s2options);
+ this->options = options.booleanOperationOptions();
+ }
+
+ // See IndexedBinaryGeographyOperator::buildIndex() for why 50 is the default value
+ // for maxEdgesPerCell
+ void buildIndex(List geog2, int maxEdgesPerCell = 50) {
+ this->geog2 = geog2;
+ IndexedBinaryGeographyOperator<List, IntegerVector>::buildIndex(geog2, maxEdgesPerCell);
+ }
+
+ IntegerVector processFeature(Rcpp::XPtr<Geography> feature, R_xlen_t i) {
+ S2ShapeIndex* index1 = feature->ShapeIndex();
+ S2ShapeIndexRegion<S2ShapeIndex> region = MakeS2ShapeIndexRegion(index1);
+
+ // build a list of candidate feature indices
+ std::unordered_set<R_xlen_t> mightIntersectIndices = findPossibleIntersections(
+ region,
+ this->geog2Index.get(),
+ this->geog2IndexSource,
+ this->maxFeatureCells
+ );
+
+ // loop through features from geog2 that might intersect feature
+ // and build a list of indices that actually intersect (based on
+ // this->actuallyIntersects(), which might perform alternative
+ // comparisons)
+ std::vector<int> actuallyIntersectIndices;
+ for (R_xlen_t j: mightIntersectIndices) {
+ SEXP item = this->geog2[j];
+ XPtr<Geography> feature2(item);
+ if (this->actuallyIntersects(index1, feature2->ShapeIndex(), i, j)) {
+ // convert to R index here + 1
+ actuallyIntersectIndices.push_back(j + 1);
+ }
+ }
+
+ // return sorted integer vector
+ std::sort(actuallyIntersectIndices.begin(), actuallyIntersectIndices.end());
+ return Rcpp::IntegerVector(actuallyIntersectIndices.begin(), actuallyIntersectIndices.end());
+ };
+
+ virtual bool actuallyIntersects(S2ShapeIndex* index1, S2ShapeIndex* index2, R_xlen_t i, R_xlen_t j) = 0;
+
+ protected:
+ List geog2;
+ S2BooleanOperation::Options options;
+ int maxFeatureCells;
+};
+
+// [[Rcpp::export]]
+List cpp_s2_may_intersect_matrix(List geog1, List geog2,
+ int maxEdgesPerCell, int maxFeatureCells, List s2options) {
+ class Op: public IndexedMatrixPredicateOperator {
+ public:
+ Op(List s2options, int maxFeatureCells):
+ IndexedMatrixPredicateOperator(s2options, maxFeatureCells) {}
+
+ bool actuallyIntersects(S2ShapeIndex* index1, S2ShapeIndex* index2, R_xlen_t i, R_xlen_t j) {
+ return true;
+ };
+ };
+
+ Op op(s2options, maxFeatureCells);
+ op.buildIndex(geog2, maxEdgesPerCell);
+ return op.processVector(geog1);
+}
+
+// [[Rcpp::export]]
+List cpp_s2_contains_matrix(List geog1, List geog2, List s2options) {
+ class Op: public IndexedMatrixPredicateOperator {
+ public:
+ Op(List s2options): IndexedMatrixPredicateOperator(s2options) {}
+ bool actuallyIntersects(S2ShapeIndex* index1, S2ShapeIndex* index2, R_xlen_t i, R_xlen_t j) {
+ return S2BooleanOperation::Contains(*index1, *index2, this->options);
+ };
+ };
+
+ Op op(s2options);
+ op.buildIndex(geog2);
+ return op.processVector(geog1);
+}
+
+// [[Rcpp::export]]
+List cpp_s2_within_matrix(List geog1, List geog2, List s2options) {
+ class Op: public IndexedMatrixPredicateOperator {
+ public:
+ Op(List s2options): IndexedMatrixPredicateOperator(s2options) {}
+ bool actuallyIntersects(S2ShapeIndex* index1, S2ShapeIndex* index2, R_xlen_t i, R_xlen_t j) {
+ // note reversed index2, index1
+ return S2BooleanOperation::Contains(*index2, *index1, this->options);
+ };
+ };
+
+ Op op(s2options);
+ op.buildIndex(geog2);
+ return op.processVector(geog1);
+}
+
+// [[Rcpp::export]]
+List cpp_s2_intersects_matrix(List geog1, List geog2, List s2options) {
+ class Op: public IndexedMatrixPredicateOperator {
+ public:
+ Op(List s2options): IndexedMatrixPredicateOperator(s2options) {}
+ bool actuallyIntersects(S2ShapeIndex* index1, S2ShapeIndex* index2, R_xlen_t i, R_xlen_t j) {
+ return S2BooleanOperation::Intersects(*index1, *index2, this->options);
+ };
+ };
+
+ Op op(s2options);
+ op.buildIndex(geog2);
+ return op.processVector(geog1);
+}
+
+// [[Rcpp::export]]
+List cpp_s2_equals_matrix(List geog1, List geog2, List s2options) {
+ class Op: public IndexedMatrixPredicateOperator {
+ public:
+ Op(List s2options): IndexedMatrixPredicateOperator(s2options) {}
+ bool actuallyIntersects(S2ShapeIndex* index1, S2ShapeIndex* index2, R_xlen_t i, R_xlen_t j) {
+ return S2BooleanOperation::Equals(*index1, *index2, this->options);
+ };
+ };
+
+ Op op(s2options);
+ op.buildIndex(geog2);
+ return op.processVector(geog1);
+}
+
+// [[Rcpp::export]]
+List cpp_s2_touches_matrix(List geog1, List geog2, List s2options) {
+ class Op: public IndexedMatrixPredicateOperator {
+ public:
+ Op(List s2options): IndexedMatrixPredicateOperator(s2options) {
+ this->closedOptions = this->options;
+ this->closedOptions.set_polygon_model(S2BooleanOperation::PolygonModel::CLOSED);
+ this->closedOptions.set_polyline_model(S2BooleanOperation::PolylineModel::CLOSED);
+
+ this->openOptions = this->options;
+ this->openOptions.set_polygon_model(S2BooleanOperation::PolygonModel::OPEN);
+ this->openOptions.set_polyline_model(S2BooleanOperation::PolylineModel::OPEN);
+ }
+
+ bool actuallyIntersects(S2ShapeIndex* index1, S2ShapeIndex* index2, R_xlen_t i, R_xlen_t j) {
+ // efficiently re-uses the index on geog2 and takes advantage of short-circuiting &&
+ return S2BooleanOperation::Intersects(*index1, *index2, this->closedOptions) &&
+ !S2BooleanOperation::Intersects(*index1, *index2, this->openOptions);
+ };
+
+ private:
+ S2BooleanOperation::Options closedOptions;
+ S2BooleanOperation::Options openOptions;
+ };
+
+ Op op(s2options);
+ op.buildIndex(geog2);
+ return op.processVector(geog1);
+}
+
+
+// ----------- brute force binary predicate operators ------------------
+
+class BruteForceMatrixPredicateOperator {
+public:
+ std::vector<S2ShapeIndex*> geog2Indices;
+ S2BooleanOperation::Options options;
+
+ BruteForceMatrixPredicateOperator() {}
+
+ BruteForceMatrixPredicateOperator(Rcpp::List s2options) {
+ GeographyOperationOptions options(s2options);
+ this->options = options.booleanOperationOptions();
+ }
+
+ List processVector(Rcpp::List geog1, Rcpp::List geog2) {
+ List output(geog1.size());
+
+ // using instead of IntegerVector because
+ // std::vector is much faster with repeated calls to .push_back()
+ std::vector<int> trueIndices;
+
+ for (R_xlen_t i = 0; i < geog1.size(); i++) {
+ trueIndices.clear();
+
+ SEXP item1 = geog1[i];
+ if (item1 == R_NilValue) {
+ output[i] = R_NilValue;
+ } else {
+ Rcpp::XPtr<Geography> feature1(item1);
+
+ for (size_t j = 0; j < geog2.size(); j++) {
+ checkUserInterrupt();
+ SEXP item2 = geog2[j];
+ if (item2 == R_NilValue) {
+ stop("Missing `y` not allowed in binary index operations");
+ }
+
+ XPtr<Geography> feature2(item2);
+
+ bool result = this->processFeature(feature1, feature2, i, j);
+ if (result) {
+ // convert to R index here (+1)
+ trueIndices.push_back(j + 1);
+ }
+ }
+
+ IntegerVector itemOut(trueIndices.size());
+ for (size_t k = 0; k < trueIndices.size(); k++) {
+ itemOut[k] = trueIndices[k];
+ }
+ output[i] = itemOut;
+ }
+ }
+
+ return output;
+ }
+
+ virtual bool processFeature(XPtr<Geography> feature1, XPtr<Geography> feature2,
+ R_xlen_t i, R_xlen_t j) = 0;
+};
+
+// [[Rcpp::export]]
+List cpp_s2_dwithin_matrix(List geog1, List geog2, double distance) {
+ class Op: public BruteForceMatrixPredicateOperator {
+ public:
+ double distance;
+ Op(double distance): distance(distance) {}
+ bool processFeature(XPtr<Geography> feature1, XPtr<Geography> feature2,
+ R_xlen_t i, R_xlen_t j) {
+ S2ClosestEdgeQuery query(feature2->ShapeIndex());
+ S2ClosestEdgeQuery::ShapeIndexTarget target(feature1->ShapeIndex());
+ return query.IsDistanceLessOrEqual(&target, S1ChordAngle::Radians(this->distance));
+ };
+ };
+
+ Op op(distance);
+ return op.processVector(geog1, geog2);
+}
+
+// ----------- distance matrix operators -------------------
+
+template<class MatrixType, class ScalarType>
+class MatrixGeographyOperator {
+public:
+ MatrixType processVector(Rcpp::List geog1, Rcpp::List geog2) {
+
+ MatrixType output(geog1.size(), geog2.size());
+
+ SEXP item1;
+ SEXP item2;
+
+ for (R_xlen_t i = 0; i < geog1.size(); i++) {
+ item1 = geog1[i];
+ if (item1 == R_NilValue) {
+ for (R_xlen_t j = 0; j < geog2.size(); j++) {
+ output(i, j) = MatrixType::get_na();
+ }
+ } else {
+ Rcpp::XPtr<Geography> feature1(item1);
+
+ for (R_xlen_t j = 0; j < geog2.size(); j++) {
+ checkUserInterrupt();
+ item2 = geog2[j];
+
+ if (item2 == R_NilValue) {
+ output(i, j) = MatrixType::get_na();
+ } else {
+ Rcpp::XPtr<Geography> feature2(item2);
+ output(i, j) = this->processFeature(feature1, feature2, i, j);
+ }
+ }
+ }
+ }
+
+ return output;
+ }
+
+ virtual ScalarType processFeature(Rcpp::XPtr<Geography> feature1,
+ Rcpp::XPtr<Geography> feature2,
+ R_xlen_t i, R_xlen_t j) = 0;
+};
+
+// [[Rcpp::export]]
+NumericMatrix cpp_s2_distance_matrix(List geog1, List geog2) {
+ class Op: public MatrixGeographyOperator<NumericMatrix, double> {
+
+ double processFeature(XPtr<Geography> feature1, XPtr<Geography> feature2,
+ R_xlen_t i, R_xlen_t j) {
+ S2ClosestEdgeQuery query(feature1->ShapeIndex());
+ S2ClosestEdgeQuery::ShapeIndexTarget target(feature2->ShapeIndex());
+ const auto& result = query.FindClosestEdge(&target);
+
+ S1ChordAngle angle = result.distance();
+ double distance = angle.ToAngle().radians();
+
+ if (distance == R_PosInf) {
+ return NA_REAL;
+ } else {
+ return distance;
+ }
+ }
+ };
+
+ Op op;
+ return op.processVector(geog1, geog2);
+}
+
+// [[Rcpp::export]]
+NumericMatrix cpp_s2_max_distance_matrix(List geog1, List geog2) {
+ class Op: public MatrixGeographyOperator<NumericMatrix, double> {
+
+ double processFeature(XPtr<Geography> feature1, XPtr<Geography> feature2,
+ R_xlen_t i, R_xlen_t j) {
+ S2FurthestEdgeQuery query(feature1->ShapeIndex());
+ S2FurthestEdgeQuery::ShapeIndexTarget target(feature2->ShapeIndex());
+ const auto& result = query.FindFurthestEdge(&target);
+
+ S1ChordAngle angle = result.distance();
+ double distance = angle.ToAngle().radians();
+
+ // returns -1 if one of the indexes is empty
+ // NA is more consistent with the BigQuery
+ // function, and makes way more sense
+ if (distance < 0) {
+ return NA_REAL;
+ } else {
+ return distance;
+ }
+ }
+ };
+
+ Op op;
+ return op.processVector(geog1, geog2);
+}
+
+
+// ----------- brute force binary predicate operators (for testing) ------------------
+
+// [[Rcpp::export]]
+List cpp_s2_contains_matrix_brute_force(List geog1, List geog2, List s2options) {
+ class Op: public BruteForceMatrixPredicateOperator {
+ public:
+ Op(List s2options): BruteForceMatrixPredicateOperator(s2options) {}
+ bool processFeature(XPtr<Geography> feature1, XPtr<Geography> feature2,
+ R_xlen_t i, R_xlen_t j) {
+ // by default Contains() will return true for Contains(x, EMPTY), which is
+ // not true in BigQuery or GEOS
+ if (feature2->IsEmpty()) {
+ return false;
+ } else {
+ return S2BooleanOperation::Contains(
+ *feature1->ShapeIndex(),
+ *feature2->ShapeIndex(),
+ this->options
+ );
+ }
+ };
+ };
+
+ Op op(s2options);
+ return op.processVector(geog1, geog2);
+}
+
+// [[Rcpp::export]]
+List cpp_s2_within_matrix_brute_force(List geog1, List geog2, List s2options) {
+ class Op: public BruteForceMatrixPredicateOperator {
+ public:
+ Op(List s2options): BruteForceMatrixPredicateOperator(s2options) {}
+ bool processFeature(XPtr<Geography> feature1, XPtr<Geography> feature2,
+ R_xlen_t i, R_xlen_t j) {
+ // note reversed index2, index1
+
+ // by default Contains() will return true for Contains(x, EMPTY), which is
+ // not true in BigQuery or GEOS
+ if (feature1->IsEmpty()) {
+ return false;
+ } else {
+ return S2BooleanOperation::Contains(
+ *feature2->ShapeIndex(),
+ *feature1->ShapeIndex(),
+ this->options
+ );
+ }
+ };
+ };
+
+ Op op(s2options);
+ return op.processVector(geog1, geog2);
+}
+
+// [[Rcpp::export]]
+List cpp_s2_intersects_matrix_brute_force(List geog1, List geog2, List s2options) {
+ class Op: public BruteForceMatrixPredicateOperator {
+ public:
+ Op(List s2options): BruteForceMatrixPredicateOperator(s2options) {}
+ bool processFeature(XPtr<Geography> feature1, XPtr<Geography> feature2,
+ R_xlen_t i, R_xlen_t j) {
+ return S2BooleanOperation::Intersects(
+ *feature1->ShapeIndex(),
+ *feature2->ShapeIndex(),
+ this->options
+ );
+ }
+ };
+
+ Op op(s2options);
+ return op.processVector(geog1, geog2);
+}
+
+// [[Rcpp::export]]
+List cpp_s2_disjoint_matrix_brute_force(List geog1, List geog2, List s2options) {
+ class Op: public BruteForceMatrixPredicateOperator {
+ public:
+ Op(List s2options): BruteForceMatrixPredicateOperator(s2options) {}
+ bool processFeature(XPtr<Geography> feature1, XPtr<Geography> feature2,
+ R_xlen_t i, R_xlen_t j) {
+ return !S2BooleanOperation::Intersects(
+ *feature1->ShapeIndex(),
+ *feature2->ShapeIndex(),
+ this->options
+ );
+ }
+ };
+
+ Op op(s2options);
+ return op.processVector(geog1, geog2);
+}
+
+// [[Rcpp::export]]
+List cpp_s2_equals_matrix_brute_force(List geog1, List geog2, List s2options) {
+ class Op: public BruteForceMatrixPredicateOperator {
+ public:
+ Op(List s2options): BruteForceMatrixPredicateOperator(s2options) {}
+ bool processFeature(XPtr<Geography> feature1, XPtr<Geography> feature2,
+ R_xlen_t i, R_xlen_t j) {
+ return S2BooleanOperation::Equals(
+ *feature1->ShapeIndex(),
+ *feature2->ShapeIndex(),
+ this->options
+ );
+ }
+ };
+
+ Op op(s2options);
+ return op.processVector(geog1, geog2);
+}
--- /dev/null
+
+#include <sstream>
+#include <Rcpp.h>
+#include "s2/s2boolean_operation.h"
+#include "s2/s2builderutil_snap_functions.h"
+#include "s2/s2builderutil_s2polygon_layer.h"
+#include "s2/s2builderutil_s2polyline_vector_layer.h"
+#include "s2/s2builderutil_s2point_vector_layer.h"
+
+// This class wraps several concepts in the S2BooleanOperation,
+// and S2Layer, parameterized such that these can be specified from R
+class GeographyOperationOptions {
+public:
+ int polygonModel;
+ int polylineModel;
+ Rcpp::List snap;
+ double snapRadius;
+ int duplicatePointEdges;
+ int duplicatePolylineEdges;
+ int duplicatePolygonEdges;
+ int polylineEdgeType;
+ int polygonEdgeType;
+ int validatePolyline;
+ int validatePolygon;
+ int polylineType;
+ int polylineSiblingPairs;
+ int simplifyEdgeChains;
+ int splitCrossingEdges;
+ int idempotent;
+
+ // Wraps options for the three layer types
+ class LayerOptions {
+ public:
+ s2builderutil::S2PointVectorLayer::Options pointLayerOptions;
+ s2builderutil::S2PolylineVectorLayer::Options polylineLayerOptions;
+ s2builderutil::S2PolygonLayer::Options polygonLayerOptions;
+ };
+
+ // deaults: use S2 defaults
+ GeographyOperationOptions(): polygonModel(-1), polylineModel(-1), snapRadius(-1) {
+ this->snap.attr("class") = "snap_identity";
+ }
+
+ // create from s2_options() object
+ GeographyOperationOptions(Rcpp::List s2options): GeographyOperationOptions() {
+ if (!Rf_inherits(s2options, "s2_options")) {
+ Rcpp::stop("`options` must be created using s2_options()");
+ }
+
+ // if these items are of an incorrect type (e.g., list() instead of int)
+ // the default errors are very difficult to diagnose.
+ try {
+ int model = s2options["model"];
+ this->polylineModel = model;
+ this->polygonModel = model;
+ } catch (std::exception& e) {
+ std::stringstream err;
+ err << "Error setting s2_options() `model`: " << e.what();
+ Rcpp::stop(err.str());
+ }
+
+ try {
+ this->snap = s2options["snap"];
+ } catch (std::exception& e) {
+ std::stringstream err;
+ err << "Error setting s2_options() `snap`: " << e.what();
+ Rcpp::stop(err.str());
+ }
+
+ try {
+ this->snapRadius = s2options["snap_radius"];
+ } catch (std::exception& e) {
+ std::stringstream err;
+ err << "Error setting s2_options() `snap_radius`: " << e.what();
+ Rcpp::stop(err.str());
+ }
+
+ try {
+ int duplicateEdges = s2options["duplicate_edges"];
+ this->duplicatePointEdges = duplicateEdges;
+ this->duplicatePolylineEdges = duplicateEdges;
+ this->duplicatePolygonEdges = duplicateEdges;
+ } catch (std::exception& e) {
+ std::stringstream err;
+ err << "Error setting s2_options() `duplicate_edges`: " << e.what();
+ Rcpp::stop(err.str());
+ }
+
+ try {
+ int edgeType = s2options["edge_type"];
+ this->polylineEdgeType = edgeType;
+ this->polygonEdgeType = edgeType;
+ } catch (std::exception& e) {
+ std::stringstream err;
+ err << "Error setting s2_options() `edge_type`: " << e.what();
+ Rcpp::stop(err.str());
+ }
+
+ try {
+ int validate = s2options["validate"];
+ this->validatePolyline = validate;
+ this->validatePolygon = validate;
+ } catch (std::exception& e) {
+ std::stringstream err;
+ err << "Error setting s2_options() `duplicate_edges`: " << e.what();
+ Rcpp::stop(err.str());
+ }
+
+ try {
+ this->polylineType = s2options["polyline_type"];
+ } catch (std::exception& e) {
+ std::stringstream err;
+ err << "Error setting s2_options() `polyline_type`: " << e.what();
+ Rcpp::stop(err.str());
+ }
+
+ try {
+ this->polylineSiblingPairs = s2options["polyline_sibling_pairs"];
+ } catch (std::exception& e) {
+ std::stringstream err;
+ err << "Error setting s2_options() `polyline_sibling_pairs`: " << e.what();
+ Rcpp::stop(err.str());
+ }
+
+ try {
+ this->simplifyEdgeChains = s2options["simplify_edge_chains"];
+ } catch (std::exception& e) {
+ std::stringstream err;
+ err << "Error setting s2_options() `simplify_edge_chains`: " << e.what();
+ Rcpp::stop(err.str());
+ }
+
+ try {
+ this->splitCrossingEdges = s2options["split_crossing_edges"];
+ } catch (std::exception& e) {
+ std::stringstream err;
+ err << "Error setting s2_options() `split_crossing_edges`: " << e.what();
+ Rcpp::stop(err.str());
+ }
+
+ try {
+ this->idempotent = s2options["idempotent"];
+ } catch (std::exception& e) {
+ std::stringstream err;
+ err << "Error setting s2_options() `idempotent`: " << e.what();
+ Rcpp::stop(err.str());
+ }
+ }
+
+ // build options for passing this to the S2BooleanOperation
+ S2BooleanOperation::Options booleanOperationOptions() {
+ S2BooleanOperation::Options options;
+ if (this->polygonModel >= 0) {
+ options.set_polygon_model(getPolygonModel(this->polygonModel));
+ }
+ if (this->polylineModel >= 0) {
+ options.set_polyline_model(getPolylineModel(this->polylineModel));
+ }
+ this->setSnapFunction<S2BooleanOperation::Options>(options);
+
+ return options;
+ }
+
+ // build options for S2Builder
+ S2Builder::Options builderOptions() {
+ S2Builder::Options options;
+ options.set_simplify_edge_chains(this->simplifyEdgeChains);
+ options.set_split_crossing_edges(this->splitCrossingEdges);
+ options.set_idempotent(this->idempotent);
+ this->setSnapFunction<S2Builder::Options>(options);
+ return options;
+ }
+
+ // build options for point, polyline, and polygon layers
+ LayerOptions layerOptions() {
+ LayerOptions out;
+
+ // point layer
+ out.pointLayerOptions.set_duplicate_edges(getDuplicateEdges(this->duplicatePointEdges));
+
+ // polyline layer
+ out.polylineLayerOptions.set_duplicate_edges(getDuplicateEdges(this->duplicatePolylineEdges));
+ out.polylineLayerOptions.set_edge_type(getEdgeType(this->polylineEdgeType));
+ out.polylineLayerOptions.set_polyline_type(getPolylineType(this->polylineType));
+ out.polylineLayerOptions.set_sibling_pairs(getSiblingPairs(this->polylineSiblingPairs));
+ out.polylineLayerOptions.set_validate(this->validatePolyline);
+
+ // always disable debugging where possible
+ out.polylineLayerOptions.set_s2debug_override(S2Debug::DISABLE);
+
+ // polygon layer
+ out.polygonLayerOptions.set_edge_type(getEdgeType(this->polygonEdgeType));
+ out.polygonLayerOptions.set_validate(this->validatePolygon);
+
+ return out;
+ }
+
+ template <class OptionsType>
+ void setSnapFunction(OptionsType& options) {
+ // S2Builder::SnapFunction is abstract and can't be returned
+ // hence the templated function
+
+ if (Rf_inherits(this->snap, "snap_identity")) {
+ s2builderutil::IdentitySnapFunction snapFunction;
+ if (this->snapRadius > 0) {
+ snapFunction.set_snap_radius(S1Angle::Radians(this->snapRadius));
+ }
+ options.set_snap_function(snapFunction);
+
+ } else if (Rf_inherits(this->snap, "snap_level")) {
+ int snapLevel = this->snap["level"];
+ s2builderutil::S2CellIdSnapFunction snapFunction(snapLevel);
+ if (this->snapRadius > 0) {
+ snapFunction.set_snap_radius(S1Angle::Radians(this->snapRadius));
+ }
+ options.set_snap_function(snapFunction);
+
+ } else if (Rf_inherits(this->snap, "snap_precision")) {
+ int exponent = snap["exponent"];
+ s2builderutil::IntLatLngSnapFunction snapFunction(exponent);
+ if (this->snapRadius > 0) {
+ snapFunction.set_snap_radius(S1Angle::Radians(this->snapRadius));
+ }
+ options.set_snap_function(snapFunction);
+
+ } else if (Rf_inherits(this->snap, "snap_distance")) {
+ double distance = snap["distance"];
+ double snapLevel = s2builderutil::S2CellIdSnapFunction::LevelForMaxSnapRadius(
+ S1Angle::Radians(distance)
+ );
+ s2builderutil::S2CellIdSnapFunction snapFunction(snapLevel);
+ if (this->snapRadius > 0) {
+ snapFunction.set_snap_radius(S1Angle::Radians(this->snapRadius));
+ }
+ options.set_snap_function(snapFunction);
+
+ } else {
+ Rcpp::stop("`snap` must be specified using s2_snap_*()");
+ }
+ }
+
+ static S2BooleanOperation::PolygonModel getPolygonModel(int model) {
+ switch (model) {
+ case 1: return S2BooleanOperation::PolygonModel::OPEN;
+ case 2: return S2BooleanOperation::PolygonModel::SEMI_OPEN;
+ case 3: return S2BooleanOperation::PolygonModel::CLOSED;
+ default:
+ std::stringstream err;
+ err << "Invalid value for polygon model: " << model;
+ Rcpp::stop(err.str());
+ }
+ }
+
+ static S2BooleanOperation::PolylineModel getPolylineModel(int model) {
+ switch (model) {
+ case 1: return S2BooleanOperation::PolylineModel::OPEN;
+ case 2: return S2BooleanOperation::PolylineModel::SEMI_OPEN;
+ case 3: return S2BooleanOperation::PolylineModel::CLOSED;
+ default:
+ std::stringstream err;
+ err << "Invalid value for polyline model: " << model;
+ Rcpp::stop(err.str());
+ }
+ }
+
+ static S2Builder::GraphOptions::DuplicateEdges getDuplicateEdges(int value) {
+ switch (value) {
+ case 0: return S2Builder::GraphOptions::DuplicateEdges::MERGE;
+ case 1: return S2Builder::GraphOptions::DuplicateEdges::KEEP;
+ default:
+ std::stringstream err;
+ err << "Invalid value for duplicate edges: " << value;
+ Rcpp::stop(err.str());
+ }
+ }
+
+ static S2Builder::GraphOptions::EdgeType getEdgeType(int value) {
+ switch (value) {
+ case 1: return S2Builder::GraphOptions::EdgeType::DIRECTED;
+ case 2: return S2Builder::GraphOptions::EdgeType::UNDIRECTED;
+ default:
+ std::stringstream err;
+ err << "Invalid value for edge type: " << value;
+ Rcpp::stop(err.str());
+ }
+ }
+
+ static S2Builder::GraphOptions::SiblingPairs getSiblingPairs(int value) {
+ switch (value) {
+ case 1: return S2Builder::GraphOptions::SiblingPairs::DISCARD;
+ case 2: return S2Builder::GraphOptions::SiblingPairs::KEEP;
+ default:
+ std::stringstream err;
+ err << "Invalid value for sibling pairs: " << value;
+ Rcpp::stop(err.str());
+ }
+ }
+
+ static S2Builder::Graph::PolylineType getPolylineType(int value) {
+ switch (value) {
+ case 1: return S2Builder::Graph::PolylineType::PATH;
+ case 2: return S2Builder::Graph::PolylineType::WALK;
+ default:
+ std::stringstream err;
+ err << "Invalid value for polylie type: " << value;
+ Rcpp::stop(err.str());
+ }
+ }
+};
--- /dev/null
+
+#include "s2/s2point.h"
+#include "s2/s2latlng.h"
+#include <Rcpp.h>
+using namespace Rcpp;
+
+// [[Rcpp::export]]
+List s2_point_from_numeric(NumericVector x, NumericVector y, NumericVector z) {
+ List output(x.size());
+
+ for (R_xlen_t i = 0; i < x.size(); i++) {
+ output[i] = XPtr<S2Point>(new S2Point(x[i], y[i], z[i]));
+ }
+
+ return output;
+}
+
+// [[Rcpp::export]]
+List s2_point_from_s2_lnglat(List s2_lnglat) {
+ List output(s2_lnglat.size());
+
+ SEXP item;
+ S2Point newItem;
+ for (R_xlen_t i = 0; i < s2_lnglat.size(); i++) {
+ item = s2_lnglat[i];
+ if (item == R_NilValue) {
+ output[i] = R_NilValue;
+ } else {
+ XPtr<S2LatLng> ptr(item);
+ newItem = ptr->Normalized().ToPoint();
+ output[i] = XPtr<S2Point>(new S2Point(newItem));
+ }
+ }
+
+ return output;
+}
+
+// [[Rcpp::export]]
+List data_frame_from_s2_point(List s2_point) {
+ NumericVector x(s2_point.size());
+ NumericVector y(s2_point.size());
+ NumericVector z(s2_point.size());
+
+ SEXP item;
+ for (R_xlen_t i = 0; i < s2_point.size(); i++) {
+ item = s2_point[i];
+ if (item == R_NilValue) {
+ x[i] = NA_REAL;
+ y[i] = NA_REAL;
+ z[i] = NA_REAL;
+ } else {
+ XPtr<S2Point> ptr(item);
+ x[i] = ptr->x();
+ y[i] = ptr->y();
+ z[i] = ptr->z();
+ }
+ }
+
+ return List::create(_["x"] = x, _["y"] = y, _["z"] = z);
+}
--- /dev/null
+
+#include "s2/s2boolean_operation.h"
+#include "s2/s2closest_edge_query.h"
+#include "s2/s2latlng_rect.h"
+#include "s2/s2polygon.h"
+#include "s2/s2testing.h"
+#include "s2/s2builderutil_snap_functions.h"
+
+#include "geography-operator.h"
+#include "s2-options.h"
+
+#include <Rcpp.h>
+using namespace Rcpp;
+
+class BinaryPredicateOperator: public BinaryGeographyOperator<LogicalVector, int> {
+public:
+ S2BooleanOperation::Options options;
+
+ BinaryPredicateOperator(List s2options) {
+ GeographyOperationOptions options(s2options);
+ this->options = options.booleanOperationOptions();
+ }
+};
+
+// [[Rcpp::export]]
+LogicalVector cpp_s2_intersects(List geog1, List geog2, List s2options) {
+ class Op: public BinaryPredicateOperator {
+ public:
+ Op(List s2options): BinaryPredicateOperator(s2options) {}
+ int processFeature(XPtr<Geography> feature1, XPtr<Geography> feature2, R_xlen_t i) {
+ return S2BooleanOperation::Intersects(
+ *feature1->ShapeIndex(),
+ *feature2->ShapeIndex(),
+ options
+ );
+ };
+ };
+
+ Op op(s2options);
+ return op.processVector(geog1, geog2);
+}
+
+// [[Rcpp::export]]
+LogicalVector cpp_s2_equals(List geog1, List geog2, List s2options) {
+ // for s2_equals(), handling polygon_model wouldn't make sense, right?
+ class Op: public BinaryPredicateOperator {
+ public:
+ Op(List s2options): BinaryPredicateOperator(s2options) {}
+ int processFeature(XPtr<Geography> feature1, XPtr<Geography> feature2, R_xlen_t i) {
+ return S2BooleanOperation::Equals(
+ *feature1->ShapeIndex(),
+ *feature2->ShapeIndex(),
+ this->options
+ );
+ }
+ };
+
+ Op op(s2options);
+ return op.processVector(geog1, geog2);
+}
+
+// [[Rcpp::export]]
+LogicalVector cpp_s2_contains(List geog1, List geog2, List s2options) {
+ class Op: public BinaryPredicateOperator {
+ public:
+ Op(List s2options): BinaryPredicateOperator(s2options) {}
+ int processFeature(XPtr<Geography> feature1, XPtr<Geography> feature2, R_xlen_t i) {
+ // by default Contains() will return true for Contains(x, EMPTY), which is
+ // not true in BigQuery or GEOS
+ if (feature2->IsEmpty()) {
+ return false;
+ } else {
+ return S2BooleanOperation::Contains(
+ *feature1->ShapeIndex(),
+ *feature2->ShapeIndex(),
+ this->options
+ );
+ }
+ }
+ };
+
+ Op op(s2options);
+ return op.processVector(geog1, geog2);
+}
+
+// [[Rcpp::export]]
+LogicalVector cpp_s2_touches(List geog1, List geog2, List s2options) {
+ class Op: public BinaryPredicateOperator {
+ public:
+ Op(List s2options): BinaryPredicateOperator(s2options) {
+ this->closedOptions = this->options;
+ this->closedOptions.set_polygon_model(S2BooleanOperation::PolygonModel::CLOSED);
+ this->closedOptions.set_polyline_model(S2BooleanOperation::PolylineModel::CLOSED);
+
+ this->openOptions = this->options;
+ this->openOptions.set_polygon_model(S2BooleanOperation::PolygonModel::OPEN);
+ this->openOptions.set_polyline_model(S2BooleanOperation::PolylineModel::OPEN);
+ }
+
+ int processFeature(XPtr<Geography> feature1, XPtr<Geography> feature2, R_xlen_t i) {
+ return S2BooleanOperation::Intersects(
+ *feature1->ShapeIndex(),
+ *feature2->ShapeIndex(),
+ this->closedOptions
+ ) &&
+ !S2BooleanOperation::Intersects(
+ *feature1->ShapeIndex(),
+ *feature2->ShapeIndex(),
+ this->openOptions
+ );
+ }
+
+ private:
+ S2BooleanOperation::Options closedOptions;
+ S2BooleanOperation::Options openOptions;
+ };
+
+ Op op(s2options);
+ return op.processVector(geog1, geog2);
+}
+
+// [[Rcpp::export]]
+LogicalVector cpp_s2_dwithin(List geog1, List geog2, NumericVector distance) {
+ if (distance.size() != geog1.size()) {
+ stop("Incompatible lengths"); // #nocov
+ }
+
+ class Op: public BinaryGeographyOperator<LogicalVector, int> {
+ public:
+ NumericVector distance;
+ Op(NumericVector distance): distance(distance) {}
+
+ int processFeature(XPtr<Geography> feature1, XPtr<Geography> feature2, R_xlen_t i) {
+ S2ClosestEdgeQuery query(feature1->ShapeIndex());
+ S2ClosestEdgeQuery::ShapeIndexTarget target(feature2->ShapeIndex());
+ return query.IsDistanceLessOrEqual(&target, S1ChordAngle::Radians(this->distance[i]));
+ }
+ };
+
+ Op op(distance);
+ return op.processVector(geog1, geog2);
+}
+
+// [[Rcpp::export]]
+LogicalVector cpp_s2_intersects_box(List geog,
+ NumericVector lng1, NumericVector lat1,
+ NumericVector lng2, NumericVector lat2,
+ IntegerVector detail,
+ List s2options) {
+
+ class Op: public UnaryGeographyOperator<LogicalVector, int> {
+ public:
+ NumericVector lng1, lat1, lng2, lat2;
+ IntegerVector detail;
+ S2BooleanOperation::Options options;
+
+ Op(NumericVector lng1, NumericVector lat1,
+ NumericVector lng2, NumericVector lat2,
+ IntegerVector detail, List s2options):
+ lng1(lng1), lat1(lat1), lng2(lng2), lat2(lat2), detail(detail) {
+
+ GeographyOperationOptions options(s2options);
+ this->options = options.booleanOperationOptions();
+ }
+
+ int processFeature(XPtr<Geography> feature, R_xlen_t i) {
+ // construct polygon
+ // this might be easier with an s2region intersection
+ double xmin = this->lng1[i];
+ double ymin = this->lat1[i];
+ double xmax = this->lng2[i];
+ double ymax = this->lat2[i];
+ int detail = this->detail[i];
+
+ if (detail < 1) {
+ stop("Can't create polygon from bounding box with detail < 1");
+ }
+
+ // can't just do xmax - xmin because these boxes can wrap around the date line
+ S2Point westEquator = S2LatLng::FromDegrees(0, xmin).Normalized().ToPoint();
+ S2Point eastEquator = S2LatLng::FromDegrees(0, xmax).Normalized().ToPoint();
+ S1ChordAngle width(westEquator, eastEquator);
+ double widthDegrees = width.degrees();
+ double deltaDegrees = widthDegrees / (double) detail;
+ double heightDegrees = ymax - ymin;
+
+ // these situations would result in an error below because of
+ // duplicate vertices
+ if (widthDegrees == 0 || heightDegrees == 0) {
+ return false;
+ }
+
+ // create polygon vertices
+ std::vector<S2Point> points(2 + 2 * detail);
+ S2LatLng vertex;
+
+ // south edge
+ for (int i = 0; i <= detail; i++) {
+ vertex = S2LatLng::FromDegrees(xmin + deltaDegrees * i, ymin).Normalized();
+ points[i] = vertex.ToPoint();
+ }
+
+ // north edge
+ for (int i = 0; i <= detail; i++) {
+ vertex = S2LatLng::FromDegrees(xmax - deltaDegrees * i, ymax).Normalized();
+ points[detail + 1 + i] = vertex.ToPoint();
+ }
+
+ // create polygon
+ std::unique_ptr<S2Loop> loop(new S2Loop());
+ loop->set_s2debug_override(S2Debug::DISABLE);
+ loop->Init(points);
+ loop->Normalize();
+
+ std::vector<std::unique_ptr<S2Loop>> loops(1);
+ loops[0] = std::move(loop);
+ S2Polygon polygon;
+ polygon.InitOriented(std::move(loops));
+
+ // test intersection
+ return S2BooleanOperation::Intersects(
+ polygon.index(),
+ *feature->ShapeIndex(),
+ this->options
+ );
+ }
+ };
+
+ Op op(lng1, lat1, lng2, lat2, detail, s2options);
+ return op.processVector(geog);
+}
--- /dev/null
+
+#include "s2/s2boolean_operation.h"
+#include "s2/s2closest_edge_query.h"
+#include "s2/s2polygon.h"
+#include "s2/s2polyline.h"
+#include "s2/s2point.h"
+#include "s2/s2error.h"
+#include "s2/s2boolean_operation.h"
+#include "s2/s2builder.h"
+#include "s2/s2builderutil_s2polygon_layer.h"
+#include "s2/s2builderutil_s2polyline_vector_layer.h"
+#include "s2/s2builderutil_s2point_vector_layer.h"
+#include "s2/s2builderutil_closed_set_normalizer.h"
+#include "s2/s2builderutil_snap_functions.h"
+#include "s2/s2shape_index_buffered_region.h"
+#include "s2/s2region_coverer.h"
+
+#include "s2-options.h"
+#include "geography-operator.h"
+#include "point-geography.h"
+#include "polyline-geography.h"
+#include "polygon-geography.h"
+#include "geography-collection.h"
+
+#include <Rcpp.h>
+using namespace Rcpp;
+
+std::unique_ptr<Geography> geographyFromLayers(std::vector<S2Point> points,
+ std::vector<std::unique_ptr<S2Polyline>> polylines,
+ std::unique_ptr<S2Polygon> polygon) {
+ // count non-empty dimensions
+ int nonEmptyDimensions = (!polygon->is_empty() + (polylines.size() > 0) + (points.size() > 0));
+
+ // return empty output
+ if (nonEmptyDimensions == 0) {
+ return absl::make_unique<GeographyCollection>();
+ }
+
+ // return mixed dimension output
+ if (nonEmptyDimensions > 1) {
+ std::vector<std::unique_ptr<Geography>> features;
+
+ if (points.size() > 0) {
+ features.push_back(absl::make_unique<PointGeography>(std::move(points)));
+ }
+
+ if (polylines.size() > 0) {
+ features.push_back(absl::make_unique<PolylineGeography>(std::move(polylines)));
+ }
+
+ if (!polygon->is_empty()) {
+ features.push_back(absl::make_unique<PolygonGeography>(std::move(polygon)));
+ }
+
+ return absl::make_unique<GeographyCollection>(std::move(features));
+ }
+
+ // return single dimension output
+ if (!polygon->is_empty()) {
+ return absl::make_unique<PolygonGeography>(std::move(polygon));
+ } else if (polylines.size() > 0) {
+ return absl::make_unique<PolylineGeography>(std::move(polylines));
+ } else {
+ return absl::make_unique<PointGeography>(std::move(points));
+ }
+}
+
+std::unique_ptr<Geography> doBooleanOperation(S2ShapeIndex* index1, S2ShapeIndex* index2,
+ S2BooleanOperation::OpType opType,
+ S2BooleanOperation::Options options,
+ GeographyOperationOptions::LayerOptions layerOptions) {
+
+ // create the data structures that will contain the output
+ std::vector<S2Point> points;
+ std::vector<std::unique_ptr<S2Polyline>> polylines;
+ std::unique_ptr<S2Polygon> polygon = absl::make_unique<S2Polygon>();
+
+ s2builderutil::LayerVector layers(3);
+ layers[0] = absl::make_unique<s2builderutil::S2PointVectorLayer>(&points, layerOptions.pointLayerOptions);
+ layers[1] = absl::make_unique<s2builderutil::S2PolylineVectorLayer>(&polylines, layerOptions.polylineLayerOptions);
+ layers[2] = absl::make_unique<s2builderutil::S2PolygonLayer>(polygon.get(), layerOptions.polygonLayerOptions);
+
+ // do the boolean operation
+ S2BooleanOperation booleanOp(
+ opType,
+ // normalizing the closed set here is required for line intersections
+ // to work as expected
+ s2builderutil::NormalizeClosedSet(std::move(layers)),
+ options
+ );
+
+ // check for errors
+ S2Error error;
+ if (!booleanOp.Build(*index1, *index2, &error)) {
+ stop(error.text());
+ }
+
+ // construct output
+ return geographyFromLayers(
+ std::move(points),
+ std::move(polylines),
+ std::move(polygon)
+ );
+}
+
+std::unique_ptr<Geography> rebuildGeography(S2ShapeIndex* index,
+ S2Builder::Options options,
+ GeographyOperationOptions::LayerOptions layerOptions) {
+ // create the builder
+ S2Builder builder(options);
+
+ // create the data structures that will contain the output
+ std::vector<S2Point> points;
+ std::vector<std::unique_ptr<S2Polyline>> polylines;
+ std::unique_ptr<S2Polygon> polygon = absl::make_unique<S2Polygon>();
+
+ // add shapes to the layer with the appropriate dimension
+ builder.StartLayer(
+ absl::make_unique<s2builderutil::S2PointVectorLayer>(&points, layerOptions.pointLayerOptions)
+ );
+ for (S2Shape* shape : *index) {
+ if (shape->dimension() == 0) {
+ builder.AddShape(*shape);
+ }
+ }
+
+ builder.StartLayer(
+ absl::make_unique<s2builderutil::S2PolylineVectorLayer>(&polylines, layerOptions.polylineLayerOptions)
+ );
+ for (S2Shape* shape : *index) {
+ if (shape->dimension() == 1) {
+ builder.AddShape(*shape);
+ }
+ }
+
+ builder.StartLayer(
+ absl::make_unique<s2builderutil::S2PolygonLayer>(polygon.get(), layerOptions.polygonLayerOptions)
+ );
+ for (S2Shape* shape : *index) {
+ if (shape->dimension() == 2) {
+ builder.AddShape(*shape);
+ }
+ }
+
+ // build the output
+ S2Error error;
+ if (!builder.Build(&error)) {
+ stop(error.text());
+ }
+
+ // construct output
+ return geographyFromLayers(
+ std::move(points),
+ std::move(polylines),
+ std::move(polygon)
+ );
+}
+
+class BooleanOperationOp: public BinaryGeographyOperator<List, SEXP> {
+public:
+ BooleanOperationOp(S2BooleanOperation::OpType opType, List s2options):
+ opType(opType) {
+ GeographyOperationOptions options(s2options);
+ this->options = options.booleanOperationOptions();
+ this->layerOptions = options.layerOptions();
+ }
+
+ SEXP processFeature(XPtr<Geography> feature1, XPtr<Geography> feature2, R_xlen_t i) {
+ std::unique_ptr<Geography> geography = doBooleanOperation(
+ feature1->ShapeIndex(),
+ feature2->ShapeIndex(),
+ this->opType,
+ this->options,
+ this->layerOptions
+ );
+
+ return Rcpp::XPtr<Geography>(geography.release());
+ }
+
+private:
+ S2BooleanOperation::OpType opType;
+ S2BooleanOperation::Options options;
+ GeographyOperationOptions::LayerOptions layerOptions;
+};
+
+// [[Rcpp::export]]
+List cpp_s2_intersection(List geog1, List geog2, List s2options) {
+ BooleanOperationOp op(S2BooleanOperation::OpType::INTERSECTION, s2options);
+ return op.processVector(geog1, geog2);
+}
+
+// [[Rcpp::export]]
+List cpp_s2_union(List geog1, List geog2, List s2options) {
+ BooleanOperationOp op(S2BooleanOperation::OpType::UNION, s2options);
+ return op.processVector(geog1, geog2);
+}
+
+// [[Rcpp::export]]
+List cpp_s2_difference(List geog1, List geog2, List s2options) {
+ BooleanOperationOp op(S2BooleanOperation::OpType::DIFFERENCE, s2options);
+ return op.processVector(geog1, geog2);
+}
+
+// [[Rcpp::export]]
+List cpp_s2_sym_difference(List geog1, List geog2, List s2options) {
+ BooleanOperationOp op(S2BooleanOperation::OpType::SYMMETRIC_DIFFERENCE, s2options);
+ return op.processVector(geog1, geog2);
+}
+
+// [[Rcpp::export]]
+List cpp_s2_union_agg(List geog, List s2options, bool naRm) {
+ GeographyOperationOptions options(s2options);
+
+ MutableS2ShapeIndex index;
+ SEXP item;
+ for (R_xlen_t i = 0; i < geog.size(); i++) {
+ item = geog[i];
+ if (item == R_NilValue && !naRm) {
+ return List::create(R_NilValue);
+ }
+
+ if (item != R_NilValue) {
+ Rcpp::XPtr<Geography> feature(item);
+ feature->BuildShapeIndex(&index);
+ }
+ }
+
+ MutableS2ShapeIndex emptyIndex;
+ std::unique_ptr<Geography> geography = doBooleanOperation(
+ &index,
+ &emptyIndex,
+ S2BooleanOperation::OpType::UNION,
+ options.booleanOperationOptions(),
+ options.layerOptions()
+ );
+
+ return List::create(Rcpp::XPtr<Geography>(geography.release()));
+}
+
+// [[Rcpp::export]]
+List cpp_s2_centroid_agg(List geog, bool naRm) {
+ S2Point cumCentroid;
+
+ SEXP item;
+ for (R_xlen_t i = 0; i < geog.size(); i++) {
+ item = geog[i];
+ if (item == R_NilValue && !naRm) {
+ return List::create(R_NilValue);
+ }
+
+ if (item != R_NilValue) {
+ Rcpp::XPtr<Geography> feature(item);
+ S2Point centroid = feature->Centroid();
+ if (centroid.Norm2() > 0) {
+ cumCentroid += centroid.Normalize();
+ }
+ }
+ }
+
+ List output(1);
+ if (cumCentroid.Norm2() == 0) {
+ output[0] = Rcpp::XPtr<Geography>(new PointGeography());
+ } else {
+ output[0] = Rcpp::XPtr<Geography>(new PointGeography(cumCentroid));
+ }
+
+ return output;
+}
+
+std::vector<S2Point> findClosestPoints(S2ShapeIndex* index1, S2ShapeIndex* index2) {
+ // see http://s2geometry.io/devguide/s2closestedgequery.html section on Modeling Accuracy:
+
+ // Find the edge from index2 that is closest to index1
+ S2ClosestEdgeQuery query1(index1);
+ query1.mutable_options()->set_include_interiors(false);
+ S2ClosestEdgeQuery::ShapeIndexTarget target1(index2);
+ auto result1 = query1.FindClosestEdge(&target1);
+
+ if (result1.edge_id() == -1) {
+ return std::vector<S2Point>();
+ }
+
+ // Get the edge from index1 (edge1) that is closest to index2.
+ S2Shape::Edge edge1 = query1.GetEdge(result1);
+
+ // Now find the edge from index2 (edge2) that is closest to edge1.
+ S2ClosestEdgeQuery query2(index2);
+ query2.mutable_options()->set_include_interiors(false);
+ S2ClosestEdgeQuery::EdgeTarget target2(edge1.v0, edge1.v1);
+ auto result2 = query2.FindClosestEdge(&target2);
+
+ // what if result2 has no edges?
+ if (result2.is_interior()) {
+ stop("S2ClosestEdgeQuery result is interior!");
+ }
+ S2Shape::Edge edge2 = query2.GetEdge(result2);
+
+ // Find the closest point pair on edge1 and edge2.
+ std::pair<S2Point, S2Point> closest = S2::GetEdgePairClosestPoints(
+ edge1.v0, edge1.v1,
+ edge2.v0, edge2.v1
+ );
+
+ std::vector<S2Point> pts(2);
+ pts[0] = closest.first;
+ pts[1] = closest.second;
+ return pts;
+}
+
+// [[Rcpp::export]]
+List cpp_s2_closest_point(List geog1, List geog2) {
+ class Op: public BinaryGeographyOperator<List, SEXP> {
+
+ SEXP processFeature(XPtr<Geography> feature1, XPtr<Geography> feature2, R_xlen_t i) {
+ std::vector<S2Point> pts = findClosestPoints(feature1->ShapeIndex(), feature2->ShapeIndex());
+
+ if (pts.size() == 0) {
+ return XPtr<Geography>(new PointGeography());
+ } else {
+ return XPtr<Geography>(new PointGeography(pts[0]));
+ }
+ }
+ };
+
+ Op op;
+ return op.processVector(geog1, geog2);
+}
+
+// [[Rcpp::export]]
+List cpp_s2_minimum_clearance_line_between(List geog1, List geog2) {
+ class Op: public BinaryGeographyOperator<List, SEXP> {
+
+ SEXP processFeature(XPtr<Geography> feature1, XPtr<Geography> feature2, R_xlen_t i) {
+ std::vector<S2Point> pts = findClosestPoints(feature1->ShapeIndex(), feature2->ShapeIndex());
+
+ if (pts.size() == 0) {
+ return XPtr<Geography>(new PolylineGeography());
+ } else if (pts[0] == pts[1]) {
+ return XPtr<Geography>(new PointGeography(pts));
+ } else {
+ std::unique_ptr<S2Polyline> polyline = absl::make_unique<S2Polyline>();
+ polyline->Init(pts);
+ std::vector<std::unique_ptr<S2Polyline>> polylines(1);
+ polylines[0] = std::move(polyline);
+ return XPtr<Geography>(new PolylineGeography(std::move(polylines)));
+ }
+ }
+ };
+
+ Op op;
+ return op.processVector(geog1, geog2);
+}
+
+// [[Rcpp::export]]
+List cpp_s2_centroid(List geog) {
+ class Op: public UnaryGeographyOperator<List, SEXP> {
+ SEXP processFeature(XPtr<Geography> feature, R_xlen_t i) {
+ S2Point centroid = feature->Centroid();
+ if (centroid.Norm2() == 0) {
+ return XPtr<Geography>(new PointGeography());
+ } else {
+ return XPtr<Geography>(new PointGeography(centroid.Normalize()));
+ }
+ }
+ };
+
+ Op op;
+ return op.processVector(geog);
+}
+
+// [[Rcpp::export]]
+List cpp_s2_boundary(List geog) {
+ class Op: public UnaryGeographyOperator<List, SEXP> {
+ SEXP processFeature(XPtr<Geography> feature, R_xlen_t i) {
+ std::unique_ptr<Geography> ptr = feature->Boundary();
+ return XPtr<Geography>(ptr.release());
+ }
+ };
+
+ Op op;
+ return op.processVector(geog);
+}
+
+// [[Rcpp::export]]
+List cpp_s2_rebuild(List geog, List s2options) {
+ class Op: public UnaryGeographyOperator<List, SEXP> {
+ public:
+ Op(List s2options) {
+ GeographyOperationOptions options(s2options);
+ this->options = options.builderOptions();
+ this->layerOptions = options.layerOptions();
+ }
+
+ SEXP processFeature(XPtr<Geography> feature, R_xlen_t i) {
+ std::unique_ptr<Geography> ptr = rebuildGeography(
+ feature->ShapeIndex(),
+ this->options,
+ this->layerOptions
+ );
+ return XPtr<Geography>(ptr.release());
+ }
+
+ private:
+ S2Builder::Options options;
+ GeographyOperationOptions::LayerOptions layerOptions;
+ };
+
+ Op op(s2options);
+ return op.processVector(geog);
+}
+
+// [[Rcpp::export]]
+List cpp_s2_buffer_cells(List geog, NumericVector distance, int maxCells, int minLevel) {
+ class Op: public UnaryGeographyOperator<List, SEXP> {
+ public:
+ NumericVector distance;
+ S2RegionCoverer coverer;
+
+ Op(NumericVector distance, int maxCells, int minLevel): distance(distance) {
+ this->coverer.mutable_options()->set_max_cells(maxCells);
+ if (minLevel > 0) {
+ this->coverer.mutable_options()->set_min_level(minLevel);
+ }
+ }
+
+ SEXP processFeature(XPtr<Geography> feature, R_xlen_t i) {
+ S2ShapeIndexBufferedRegion region;
+ region.Init(feature->ShapeIndex(), S1ChordAngle::Radians(this->distance[i]));
+
+ S2CellUnion cellUnion;
+ cellUnion = coverer.GetCovering(region);
+
+ std::unique_ptr<S2Polygon> polygon = absl::make_unique<S2Polygon>();
+ polygon->InitToCellUnionBorder(cellUnion);
+
+ return XPtr<PolygonGeography>(new PolygonGeography(std::move(polygon)));
+ }
+ };
+
+ Op op(distance, maxCells, minLevel);
+ return op.processVector(geog);
+}
--- /dev/null
+#include <Rcpp.h>
+using namespace Rcpp;
+
+class XPtrTest {
+public:
+ XPtrTest() {
+ try {
+ Rcout << "Allocating XPtrTest at " << this << "\n";
+ } catch (std::exception& error) {
+
+ }
+ }
+
+ void test() {
+ Rcout << "test() on XPtrTest at " << this << "\n";
+ }
+
+ ~XPtrTest() {
+ try {
+ Rcout << "Destroying XPtrTest at " << this << "\n";
+ } catch (std::exception& error) {
+
+ }
+ }
+};
+
+// [[Rcpp::export]]
+List s2_xptr_test(R_xlen_t size) {
+ List output(size);
+ for (R_xlen_t i = 0; i < size; i++) {
+ output[i] = XPtr<XPtrTest>(new XPtrTest());
+ }
+ return output;
+}
+
+// [[Rcpp::export]]
+void s2_xptr_test_op(List s2_xptr_test) {
+ SEXP item;
+ for (R_xlen_t i = 0; i < s2_xptr_test.size(); i++) {
+ item = s2_xptr_test[i];
+ if (item == R_NilValue) {
+ Rcout << "Item is NULL\n";
+ } else {
+ // the general idea is to make sure that this operation doesn't copy
+ // the pointer or cause it to get destroyed
+ XPtr<XPtrTest> ptr(item);
+ ptr->test();
+ }
+ }
+}
--- /dev/null
+// Copyright 2002 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+
+#include "s2/base/stringprintf.h"
+
+#include <cerrno>
+#include <cstdarg> // For va_list and related operations
+#include <cstdio> // MSVC requires this for _vsnprintf
+#include <vector>
+
+#include "s2/base/logging.h"
+
+#ifdef _MSC_VER
+enum { IS__MSC_VER = 1 };
+#else
+enum { IS__MSC_VER = 0 };
+#endif
+
+void StringAppendV(string* dst, const char* format, va_list ap) {
+ // First try with a small fixed size buffer
+ static const int kSpaceLength = 1024;
+ char space[kSpaceLength];
+
+ // It's possible for methods that use a va_list to invalidate
+ // the data in it upon use. The fix is to make a copy
+ // of the structure before using it and use that copy instead.
+ va_list backup_ap;
+ va_copy(backup_ap, ap);
+ int result = vsnprintf(space, kSpaceLength, format, backup_ap);
+ va_end(backup_ap);
+
+ if (result < kSpaceLength) {
+ if (result >= 0) {
+ // Normal case -- everything fit.
+ dst->append(space, result);
+ return;
+ }
+
+ if (IS__MSC_VER) {
+ // Error or MSVC running out of space. MSVC 8.0 and higher
+ // can be asked about space needed with the special idiom below:
+ va_copy(backup_ap, ap);
+ result = vsnprintf(nullptr, 0, format, backup_ap);
+ va_end(backup_ap);
+ }
+
+ if (result < 0) {
+ // Just an error.
+ return;
+ }
+ }
+
+ // Increase the buffer size to the size requested by vsnprintf,
+ // plus one for the closing \0.
+ int length = result+1;
+ char* buf = new char[length];
+
+ // Restore the va_list before we use it again
+ va_copy(backup_ap, ap);
+ result = vsnprintf(buf, length, format, backup_ap);
+ va_end(backup_ap);
+
+ if (result >= 0 && result < length) {
+ // It fit
+ dst->append(buf, result);
+ }
+ delete[] buf;
+}
+
+
+string StringPrintf(const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ string result;
+ StringAppendV(&result, format, ap);
+ va_end(ap);
+ return result;
+}
+
+const string& SStringPrintf(string* dst, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ dst->clear();
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+ return *dst;
+}
+
+void StringAppendF(string* dst, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+}
--- /dev/null
+// Copyright 2008 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+//
+// Architecture-neutral plug compatible replacements for strtol() friends.
+// See strtoint.h for details on how to use this component.
+//
+
+#include <cerrno>
+#include <climits>
+#include <limits>
+
+#include "s2/base/integral_types.h"
+#include "s2/base/port.h"
+#include "s2/base/strtoint.h"
+
+// Replacement strto[u]l functions that have identical overflow and underflow
+// characteristics for both ILP-32 and LP-64 platforms, including errno
+// preservation for error-free calls.
+int32 strto32_adapter(const char *nptr, char **endptr, int base) {
+ const int saved_errno = errno;
+ errno = 0;
+ const long result = strtol(nptr, endptr, base);
+ if (errno == ERANGE && result == LONG_MIN) {
+ return std::numeric_limits<int32>::min();
+ } else if (errno == ERANGE && result == LONG_MAX) {
+ return std::numeric_limits<int32>::max();
+ } else if (errno == 0 && result < std::numeric_limits<int32>::min()) {
+ errno = ERANGE;
+ return std::numeric_limits<int32>::min();
+ } else if (errno == 0 && result > std::numeric_limits<int32>::max()) {
+ errno = ERANGE;
+ return std::numeric_limits<int32>::max();
+ }
+ if (errno == 0)
+ errno = saved_errno;
+ return static_cast<int32>(result);
+}
+
+uint32 strtou32_adapter(const char *nptr, char **endptr, int base) {
+ const int saved_errno = errno;
+ errno = 0;
+ const unsigned long result = strtoul(nptr, endptr, base);
+ if (errno == ERANGE && result == ULONG_MAX) {
+ return std::numeric_limits<uint32>::max();
+ } else if (errno == 0 && result > std::numeric_limits<uint32>::max()) {
+ errno = ERANGE;
+ return std::numeric_limits<uint32>::max();
+ }
+ if (errno == 0)
+ errno = saved_errno;
+ return static_cast<uint32>(result);
+}
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/encoded_s2cell_id_vector.h"
+
+using absl::Span;
+using std::max;
+using std::min;
+using std::vector;
+
+namespace s2coding {
+
+void EncodeS2CellIdVector(Span<const S2CellId> v, Encoder* encoder) {
+ // v[i] is encoded as (base + (deltas[i] << shift)).
+ //
+ // "base" consists of 0-7 bytes, and is always shifted so that its bytes are
+ // the most-significant bytes of a uint64.
+ //
+ // "deltas" is an EncodedUintVector<uint64>, which means that all deltas
+ // have a fixed-length encoding determined by the largest delta.
+ //
+ // "shift" is in the range 0..56. The shift value is odd only if all
+ // S2CellIds are at the same level, in which case the bit at position
+ // (shift - 1) is automatically set to 1 in "base".
+ //
+ // "base" (3 bits) and "shift" (6 bits) are encoded in either one or two
+ // bytes as follows:
+ //
+ // - if (shift <= 4 or shift is even), then 1 byte
+ // - otherwise 2 bytes
+ //
+ // Note that (shift == 1) means that all S2CellIds are leaf cells, and
+ // (shift == 2) means that all S2CellIds are at level 29.
+ //
+ // The full encoded format is as follows:
+ //
+ // Byte 0, bits 0-2: base length (0-7 bytes)
+ // Byte 0, bits 3-7: encoded shift (see below)
+ // Byte 1: extended shift code (only needed for odd shifts >= 5)
+ // Followed by 0-7 bytes of "base"
+ // Followed by an EncodedUintVector of deltas.
+
+ uint64 v_or = 0, v_and = ~0ULL, v_min = ~0ULL, v_max = 0;
+ for (auto cellid : v) {
+ v_or |= cellid.id();
+ v_and &= cellid.id();
+ v_min = min(v_min, cellid.id());
+ v_max = max(v_max, cellid.id());
+ }
+ // These variables represent the values that will used during encoding.
+ uint64 e_base = 0; // Base value.
+ int e_base_len = 0; // Number of bytes to represent "base".
+ int e_shift = 0; // Delta shift.
+ int e_max_delta_msb = 0; // Bit position of the MSB of the largest delta.
+ if (v_or > 0) {
+ // We only allow even shifts, unless all values have the same low bit (in
+ // which case the shift is odd and the preceding bit is implicitly on).
+ // There is no point in allowing shifts > 56 since deltas are encoded in
+ // at least 1 byte each.
+ e_shift = min(56, Bits::FindLSBSetNonZero64(v_or) & ~1);
+ if (v_and & (1ULL << e_shift)) ++e_shift; // All S2CellIds same level.
+
+ // "base" consists of the "base_len" most significant bytes of the minimum
+ // S2CellId. We consider all possible values of "base_len" (0..7) and
+ // choose the one that minimizes the total encoding size.
+ uint64 e_bytes = ~0ULL; // Best encoding size so far.
+ for (int len = 0; len < 8; ++len) {
+ // "t_base" is the base value being tested (first "len" bytes of v_min).
+ // "t_max_delta_msb" is the most-significant bit position of the largest
+ // delta (or zero if there are no deltas, i.e. if v.size() == 0).
+ // "t_bytes" is the total size of the variable portion of the encoding.
+ uint64 t_base = v_min & ~(~0ULL >> (8 * len));
+ int t_max_delta_msb =
+ max(0, Bits::Log2Floor64((v_max - t_base) >> e_shift));
+ uint64 t_bytes = len + v.size() * ((t_max_delta_msb >> 3) + 1);
+ if (t_bytes < e_bytes) {
+ e_base = t_base;
+ e_base_len = len;
+ e_max_delta_msb = t_max_delta_msb;
+ e_bytes = t_bytes;
+ }
+ }
+ // It takes one extra byte to encode odd shifts (i.e., the case where all
+ // S2CellIds are at the same level), so check whether we can get the same
+ // encoding size per delta using an even shift.
+ if ((e_shift & 1) && (e_max_delta_msb & 7) != 7) --e_shift;
+ }
+ S2_DCHECK_LE(e_base_len, 7);
+ S2_DCHECK_LE(e_shift, 56);
+ encoder->Ensure(2 + e_base_len);
+
+ // As described above, "shift" and "base_len" are encoded in 1 or 2 bytes.
+ // "shift_code" is 5 bits:
+ // values <= 28 represent even shifts in the range 0..56
+ // values 29, 30 represent odd shifts 1 and 3
+ // value 31 indicates that the shift is odd and encoded in the next byte
+ int shift_code = e_shift >> 1;
+ if (e_shift & 1) shift_code = min(31, shift_code + 29);
+ encoder->put8((shift_code << 3) | e_base_len);
+ if (shift_code == 31) {
+ encoder->put8(e_shift >> 1); // Shift is always odd, so 3 bits unused.
+ }
+ // Encode the "base_len" most-significant bytes of "base".
+ uint64 base_bytes = e_base >> (64 - 8 * max(1, e_base_len));
+ EncodeUintWithLength<uint64>(base_bytes, e_base_len, encoder);
+
+ // Finally, encode the vector of deltas.
+ vector<uint64> deltas;
+ deltas.reserve(v.size());
+ for (auto cellid : v) {
+ deltas.push_back((cellid.id() - e_base) >> e_shift);
+ }
+ EncodeUintVector<uint64>(deltas, encoder);
+}
+
+bool EncodedS2CellIdVector::Init(Decoder* decoder) {
+ // All encodings have at least 2 bytes (one for our header and one for the
+ // EncodedUintVector header), so this is safe.
+ if (decoder->avail() < 2) return false;
+
+ // Invert the encoding of (shift_code, base_len) described above.
+ int code_plus_len = decoder->get8();
+ int shift_code = code_plus_len >> 3;
+ if (shift_code == 31) {
+ shift_code = 29 + decoder->get8();
+ }
+ // Decode the "base_len" most-significant bytes of "base".
+ int base_len = code_plus_len & 7;
+ if (!DecodeUintWithLength(base_len, decoder, &base_)) return false;
+ base_ <<= 64 - 8 * max(1, base_len);
+
+ // Invert the encoding of "shift_code" described above.
+ if (shift_code >= 29) {
+ shift_ = 2 * (shift_code - 29) + 1;
+ base_ |= 1ULL << (shift_ - 1);
+ } else {
+ shift_ = 2 * shift_code;
+ }
+ return deltas_.Init(decoder);
+}
+
+vector<S2CellId> EncodedS2CellIdVector::Decode() const {
+ vector<S2CellId> result(size());
+ for (int i = 0; i < size(); ++i) {
+ result[i] = (*this)[i];
+ }
+ return result;
+}
+
+} // namespace s2coding
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/encoded_s2point_vector.h"
+
+#include "s2/third_party/absl/base/internal/unaligned_access.h"
+#include "s2/util/bits/bits.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2coords.h"
+
+using absl::MakeSpan;
+using absl::Span;
+using std::max;
+using std::min;
+using std::vector;
+
+namespace s2coding {
+
+// Like util_bits::InterleaveUint32, but interleaves bit pairs rather than
+// individual bits. This format is faster to decode than the fully interleaved
+// format, and produces the same results for our use case.
+inline uint64 InterleaveUint32BitPairs(const uint32 val0, const uint32 val1) {
+ uint64 v0 = val0, v1 = val1;
+ v0 = (v0 | (v0 << 16)) & 0x0000ffff0000ffff;
+ v1 = (v1 | (v1 << 16)) & 0x0000ffff0000ffff;
+ v0 = (v0 | (v0 << 8)) & 0x00ff00ff00ff00ff;
+ v1 = (v1 | (v1 << 8)) & 0x00ff00ff00ff00ff;
+ v0 = (v0 | (v0 << 4)) & 0x0f0f0f0f0f0f0f0f;
+ v1 = (v1 | (v1 << 4)) & 0x0f0f0f0f0f0f0f0f;
+ v0 = (v0 | (v0 << 2)) & 0x3333333333333333;
+ v1 = (v1 | (v1 << 2)) & 0x3333333333333333;
+ return v0 | (v1 << 2);
+}
+
+// This code is about 50% faster than util_bits::DeinterleaveUint32, which
+// uses a lookup table. The speed advantage is expected to be even larger in
+// code that mixes bit interleaving with other significant operations since it
+// doesn't require keeping a 256-byte lookup table in the L1 data cache.
+inline void DeinterleaveUint32BitPairs(uint64 code,
+ uint32 *val0, uint32 *val1) {
+ uint64 v0 = code, v1 = code >> 2;
+ v0 &= 0x3333333333333333;
+ v0 |= v0 >> 2;
+ v1 &= 0x3333333333333333;
+ v1 |= v1 >> 2;
+ v0 &= 0x0f0f0f0f0f0f0f0f;
+ v0 |= v0 >> 4;
+ v1 &= 0x0f0f0f0f0f0f0f0f;
+ v1 |= v1 >> 4;
+ v0 &= 0x00ff00ff00ff00ff;
+ v0 |= v0 >> 8;
+ v1 &= 0x00ff00ff00ff00ff;
+ v1 |= v1 >> 8;
+ v0 &= 0x0000ffff0000ffff;
+ v0 |= v0 >> 16;
+ v1 &= 0x0000ffff0000ffff;
+ v1 |= v1 >> 16;
+ *val0 = v0;
+ *val1 = v1;
+}
+
+// Forward declarations.
+void EncodeS2PointVectorFast(Span<const S2Point> points, Encoder* encoder);
+void EncodeS2PointVectorCompact(Span<const S2Point> points, Encoder* encoder);
+
+// To save space (especially for vectors of length 0, 1, and 2), the encoding
+// format is encoded in the low-order 3 bits of the vector size. Up to 7
+// encoding formats are supported (only 2 are currently defined). Additional
+// formats could be supported by using "7" as an overflow indicator and
+// encoding the actual format separately, but it seems unlikely we will ever
+// need to do that.
+static const int kEncodingFormatBits = 3;
+static const uint8 kEncodingFormatMask = (1 << kEncodingFormatBits) - 1;
+
+void EncodeS2PointVector(Span<const S2Point> points, CodingHint hint,
+ Encoder* encoder) {
+ switch (hint) {
+ case CodingHint::FAST:
+ return EncodeS2PointVectorFast(points, encoder);
+
+ case CodingHint::COMPACT:
+ return EncodeS2PointVectorCompact(points, encoder);
+
+ default:
+ S2_LOG(DFATAL) << "Unknown CodingHint: " << static_cast<int>(hint);
+ }
+}
+
+bool EncodedS2PointVector::Init(Decoder* decoder) {
+ if (decoder->avail() < 1) return false;
+
+ // Peek at the format but don't advance the decoder; the format-specific
+ // Init functions will do that.
+ format_ = static_cast<Format>(*decoder->ptr() & kEncodingFormatMask);
+ switch (format_) {
+ case UNCOMPRESSED:
+ return InitUncompressedFormat(decoder);
+
+ case CELL_IDS:
+ return InitCellIdsFormat(decoder);
+
+ default:
+ return false;
+ }
+}
+
+vector<S2Point> EncodedS2PointVector::Decode() const {
+ vector<S2Point> points;
+ points.reserve(size_);
+ for (int i = 0; i < size_; ++i) {
+ points.push_back((*this)[i]);
+ }
+ return points;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// UNCOMPRESSED Encoding Format
+//////////////////////////////////////////////////////////////////////////////
+
+// Encodes a vector of points, optimizing for (encoding and decoding) speed.
+void EncodeS2PointVectorFast(Span<const S2Point> points, Encoder* encoder) {
+#ifndef IS_LITTLE_ENDIAN
+ S2_LOG(FATAL) << "Not implemented on big-endian architectures";
+#endif
+
+ // This function always uses the UNCOMPRESSED encoding. The header consists
+ // of a varint64 in the following format:
+ //
+ // bits 0-2: encoding format (UNCOMPRESSED)
+ // bits 3-63: vector size
+ //
+ // This is followed by an array of S2Points in little-endian order.
+ encoder->Ensure(Varint::kMax64 + points.size() * sizeof(S2Point));
+ uint64 size_format = (points.size() << kEncodingFormatBits |
+ EncodedS2PointVector::UNCOMPRESSED);
+ encoder->put_varint64(size_format);
+ encoder->putn(points.data(), points.size() * sizeof(S2Point));
+}
+
+bool EncodedS2PointVector::InitUncompressedFormat(Decoder* decoder) {
+#if !defined(IS_LITTLE_ENDIAN) || defined(__arm__) || \
+ defined(ABSL_INTERNAL_NEED_ALIGNED_LOADS)
+ // TODO(ericv): Make this work on platforms that don't support unaligned
+ // 64-bit little-endian reads, e.g. by falling back to
+ //
+ // bit_cast<double>(little_endian::Load64()).
+ //
+ // Maybe the compiler is smart enough that we can do this all the time,
+ // but more likely we will need two cases using the #ifdef above.
+ // (Note that even ARMv7 does not support unaligned 64-bit loads.)
+ S2_LOG(DFATAL) << "Needs architecture with 64-bit little-endian unaligned loads";
+ return false;
+#endif
+
+ uint64 size;
+ if (!decoder->get_varint64(&size)) return false;
+ size >>= kEncodingFormatBits;
+
+ // Note that the encoding format supports up to 2**59 vertices, but we
+ // currently only support decoding up to 2**32 vertices.
+ if (size > std::numeric_limits<uint32>::max()) return false;
+ size_ = size;
+
+ size_t bytes = size_t{size_} * sizeof(S2Point);
+ if (decoder->avail() < bytes) return false;
+
+ uncompressed_.points = reinterpret_cast<const S2Point*>(decoder->ptr());
+ decoder->skip(bytes);
+ return true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// CELL_IDS Encoding Format
+//////////////////////////////////////////////////////////////////////////////
+
+// Represents a point that can be encoded as an S2CellId center.
+// (If such an encoding is not possible then level < 0.)
+struct CellPoint {
+ // Constructor necessary in order to narrow "int" arguments to "int8".
+ CellPoint(int level, int face, uint32 si, uint32 ti)
+ : level(level), face(face), si(si), ti(ti) {}
+
+ int8 level, face;
+ uint32 si, ti;
+};
+
+// S2CellIds are represented in a special 64-bit format and are encoded in
+// fixed-size blocks. kBlockSize represents the number of values per block.
+// Block sizes of 4, 8, 16, and 32 were tested and kBlockSize == 16 seems to
+// offer the best compression. (Note that kBlockSize == 32 requires some code
+// modifications which have since been removed.)
+constexpr int kBlockShift = 4;
+constexpr size_t kBlockSize = 1 << kBlockShift;
+
+// Used to indicate that a point must be encoded as an exception (a 24-byte
+// S2Point) rather than as an S2CellId.
+constexpr uint64 kException = ~0ULL;
+
+// Represents the encoding parameters to be used for a given block (consisting
+// of kBlockSize encodable 64-bit values). See below.
+struct BlockCode {
+ int delta_bits; // Delta length in bits (multiple of 4)
+ int offset_bits; // Offset length in bits (multiple of 8)
+ int overlap_bits; // {Delta, Offset} overlap in bits (0 or 4)
+};
+
+// Returns a bit mask with "n" low-order 1 bits, for 0 <= n <= 64.
+inline uint64 BitMask(int n) {
+ return (n == 0) ? 0 : (~0ULL >> (64 - n));
+}
+
+// Returns the maximum number of bits per value at the given S2CellId level.
+inline int MaxBitsForLevel(int level) {
+ return 2 * level + 3;
+}
+
+// Returns the number of bits that "base" should be right-shifted in order to
+// encode only its leading "base_bits" bits, assuming that all points are
+// encoded at the given S2CellId level.
+inline int BaseShift(int level, int base_bits) {
+ return max(0, MaxBitsForLevel(level) - base_bits);
+}
+
+// Forward declarations.
+int ChooseBestLevel(Span<const S2Point> points, vector<CellPoint>* cell_points);
+vector<uint64> ConvertCellsToValues(const vector<CellPoint>& cell_points,
+ int level, bool* have_exceptions);
+uint64 ChooseBase(const vector<uint64>& values, int level, bool have_exceptions,
+ int* base_bits);
+BlockCode GetBlockCode(Span<const uint64> values, uint64 base,
+ bool have_exceptions);
+
+// Encodes a vector of points, optimizing for space.
+void EncodeS2PointVectorCompact(Span<const S2Point> points, Encoder* encoder) {
+ // OVERVIEW
+ // --------
+ //
+ // We attempt to represent each S2Point as the center of an S2CellId. All
+ // S2CellIds must be at the same level. Any points that cannot be encoded
+ // exactly as S2CellId centers are stored as exceptions using 24 bytes each.
+ // If there are so many exceptions that the CELL_IDS encoding does not save
+ // significant space, we give up and use the uncompressed encoding.
+ //
+ // The first step is to choose the best S2CellId level. This requires
+ // converting each point to (face, si, ti) coordinates and checking whether
+ // the point can be represented exactly as an S2CellId center at some level.
+ // We then build a histogram of S2CellId levels (just like the similar code
+ // in S2Polygon::Encode) and choose the best level (or give up, if there are
+ // not enough S2CellId-encodable points).
+ //
+ // The simplest approach would then be to take all the S2CellIds and
+ // right-shift them to remove all the constant bits at the chosen level.
+ // This would give the best spatial locality and hence the smallest deltas.
+ // However instead we give up some spatial locality and use the similar but
+ // faster transformation described below.
+ //
+ // Each encodable point is first converted to the (sj, tj) representation
+ // defined below:
+ //
+ // sj = (((face & 3) << 30) | (si >> 1)) >> (30 - level);
+ // tj = (((face & 4) << 29) | ti) >> (31 - level);
+ //
+ // These two values encode the (face, si, ti) tuple using (2 * level + 3)
+ // bits. To see this, recall that "si" and "ti" are 31-bit values that all
+ // share a common suffix consisting of a "1" bit followed by (30 - level)
+ // "0" bits. The code above right-shifts these values to remove the
+ // constant bits and then prepends the bits for "face", yielding a total of
+ // (level + 2) bits for "sj" and (level + 1) bits for "tj".
+ //
+ // We then combine (sj, tj) into one 64-bit value by interleaving bit pairs:
+ //
+ // v = InterleaveBitPairs(sj, tj);
+ //
+ // (We could also interleave individual bits, but it is faster this way.)
+ // The result is similar to right-shifting an S2CellId by (61 - 2 * level),
+ // except that it is faster to decode and the spatial locality is not quite
+ // as good.
+ //
+ // The 64-bit values are divided into blocks of size 8, and then each value is
+ // encoded as the sum of a base value, a per-block offset, and a per-value
+ // delta within that block:
+ //
+ // v[i,j] = base + offset[i] + delta[i, j]
+ //
+ // where "i" represents a block and "j" represents an entry in that block.
+ //
+ // The deltas for each block are encoded using a fixed number of 4-bit nibbles
+ // (1-16 nibbles per delta). This allows any delta to be accessed in constant
+ // time.
+ //
+ // The "offset" for each block is a 64-bit value encoded in 0-8 bytes. The
+ // offset is left-shifted such that it overlaps the deltas by a configurable
+ // number of bits (either 0 or 4), called the "overlap". The overlap and
+ // offset length (0-8 bytes) are specified per block. The reason for the
+ // overlap is that it allows fewer delta bits to be used in some cases. For
+ // example if base == 0 and the range within a block is 0xf0 to 0x110, then
+ // rather than using 12-bits deltas with an offset of 0, the overlap lets us
+ // use 8-bits deltas with an offset of 0xf0 (saving 7 bytes per block).
+ //
+ // The global minimum value "base" is encoded using 0-7 bytes starting with
+ // the most-significant non-zero bit possible for the chosen level. For
+ // example, if (level == 7) then the encoded values have at most 17 bits, so
+ // if "base" is encoded in 1 byte then it is shifted to occupy bits 9-16.
+ //
+ // Example: at level == 15, there are at most 33 non-zero value bits. The
+ // following shows the bit positions covered by "base", "offset", and "delta"
+ // assuming that "base" and "offset" are encoded in 2 bytes each, deltas are
+ // encoded in 2 nibbles (1 byte) each, and "overlap" is 4 bits:
+ //
+ // Base: 1111111100000000-----------------
+ // Offset: -------------1111111100000000----
+ // Delta: -------------------------00000000
+ // Overlap: ^^^^
+ //
+ // The numbers (0 or 1) in this diagram denote the byte number of the encoded
+ // value. Notice that "base" is shifted so that it starts at the leftmost
+ // possible bit, "delta" always starts at the rightmost possible bit (bit 0),
+ // and "offset" is shifted so that it overlaps "delta" by the chosen "overlap"
+ // (either 0 or 4 bits). Also note that all of these values are summed, and
+ // therefore each value can affect higher-order bits due to carries.
+ //
+ // NOTE(ericv): Encoding deltas in 4-bit rather than 8-bit length increments
+ // reduces encoded sizes by about 7%. Allowing a 4-bit overlap between the
+ // offset and deltas reduces encoded sizes by about 1%. Both optimizations
+ // make the code more complex but don't affect running times significantly.
+ //
+ // ENCODING DETAILS
+ // ----------------
+ //
+ // Now we can move on to the actual encodings. First, there is a 2 byte
+ // header encoded as follows:
+ //
+ // Byte 0, bits 0-2: encoding_format (CELL_IDS)
+ // Byte 0, bit 3: have_exceptions
+ // Byte 0, bits 4-7: (last_block_size - 1)
+ // Byte 1, bits 0-2: base_bytes
+ // Byte 1, bits 3-7: level (0-30)
+ //
+ // This is followed by an EncodedStringVector containing the encoded blocks.
+ // Each block contains kBlockSize (8) values. The total size of the
+ // EncodeS2PointVector is not stored explicity, but instead is calculated as
+ //
+ // num_values == kBlockSize * (num_blocks - 1) + last_block_size .
+ //
+ // (An empty vector has num_blocks == 0 and last_block_size == kBlockSize.)
+ //
+ // Each block starts with a 1 byte header containing the following:
+ //
+ // Byte 0, bits 0-2: (offset_bytes - overlap_nibbles)
+ // Byte 0, bit 3: overlap_nibbles
+ // Byte 0, bits 4-7: (delta_nibbles - 1)
+ //
+ // "overlap_nibbles" is either 0 or 1 (indicating an overlap of 0 or 4 bits),
+ // while "offset_bytes" is in the range 0-8 (indicating the number of bytes
+ // used to encode the offset for this block). Note that some combinations
+ // cannot be encoded: in particular, offset_bytes == 0 can only be encoded
+ // with an overlap of 0 bits, and offset_bytes == 8 can only be encoded with
+ // an overlap of 4 bits. This allows us to encode offset lengths of 0-8
+ // rather than just 0-7 without using an extra bit. (Note that the
+ // combinations that can't be encoded are not useful anyway.)
+ //
+ // The header is followed by "offset_bytes" bytes for the offset, and then
+ // (4 * delta_nibbles) bytes for the deltas.
+ //
+ // If there are any points that could not be represented as S2CellIds, then
+ // "have_exceptions" in the header is true. In that case the delta values
+ // within each block are encoded as (delta + 8), and values 0-7 are used to
+ // represent exceptions. If a block has exceptions, they are encoded
+ // immediately following the array of deltas, and are referenced by encoding
+ // the corresponding exception index (0-7) as the delta.
+ //
+ // TODO(ericv): A vector containing a single leaf cell is currently encoded as
+ // 13 bytes (2 byte header, 7 byte base, 1 byte block count, 1 byte block
+ // length, 1 byte block header, 1 byte delta). However if this case occurs
+ // often, a better solution would be implement a separate format that encodes
+ // the leading k bytes of an S2CellId. It would have a one-byte header
+ // consisting of the encoding format (3 bits) and the number of bytes encoded
+ // (3 bits), followed by the S2CellId bytes. The extra 2 header bits could be
+ // used to store single points using other encodings, e.g. E7.
+ //
+ // If we wind up using 8-value blocks, we could also use the extra bit in the
+ // first byte of the header to indicate that there is only one value, and
+ // then skip the 2nd byte of header and the EncodedStringVector. But this
+ // would be messy because it also requires special cases while decoding.
+ // Essentially this would be a sub-format within the CELL_IDS format.
+
+ // 1. Compute (level, face, si, ti) for each point, build a histogram of
+ // levels, and determine the optimal level to use for encoding (if any).
+ vector<CellPoint> cell_points;
+ int level = ChooseBestLevel(points, &cell_points);
+ if (level < 0) {
+ return EncodeS2PointVectorFast(points, encoder);
+ }
+
+ // 2. Convert the points into encodable 64-bit values. We don't use the
+ // S2CellId itself because it requires a somewhat more complicated bit
+ // interleaving operation.
+ //
+ // TODO(ericv): Benchmark using shifted S2CellIds instead.
+ bool have_exceptions;
+ vector<uint64> values = ConvertCellsToValues(cell_points, level,
+ &have_exceptions);
+
+ // 3. Choose the global encoding parameter "base" (consisting of the bit
+ // prefix shared by all values to be encoded).
+ int base_bits;
+ uint64 base = ChooseBase(values, level, have_exceptions, &base_bits);
+
+ // Now encode the output, starting with the 2-byte header (see above).
+ int num_blocks = (values.size() + kBlockSize - 1) >> kBlockShift;
+ int base_bytes = base_bits >> 3;
+ encoder->Ensure(2 + base_bytes);
+ int last_block_count = values.size() - kBlockSize * (num_blocks - 1);
+ S2_DCHECK_GE(last_block_count, 0);
+ S2_DCHECK_LE(last_block_count, kBlockSize);
+ S2_DCHECK_LE(base_bytes, 7);
+ S2_DCHECK_LE(level, 30);
+ encoder->put8(EncodedS2PointVector::CELL_IDS |
+ (have_exceptions << 3) |
+ ((last_block_count - 1) << 4));
+ encoder->put8(base_bytes | (level << 3));
+
+ // Next we encode 0-7 bytes of "base".
+ int base_shift = BaseShift(level, base_bits);
+ EncodeUintWithLength(base >> base_shift, base_bytes, encoder);
+
+ // Now we encode the contents of each block.
+ StringVectorEncoder blocks;
+ vector<S2Point> exceptions;
+ uint64 offset_bytes_sum = 0;
+ uint64 delta_nibbles_sum = 0;
+ uint64 exceptions_sum = 0;
+ for (int i = 0; i < values.size(); i += kBlockSize) {
+ int block_size = min(kBlockSize, values.size() - i);
+ BlockCode code = GetBlockCode(MakeSpan(&values[i], block_size),
+ base, have_exceptions);
+
+ // Encode the one-byte block header (see above).
+ Encoder* block = blocks.AddViaEncoder();
+ int offset_bytes = code.offset_bits >> 3;
+ int delta_nibbles = code.delta_bits >> 2;
+ int overlap_nibbles = code.overlap_bits >> 2;
+ block->Ensure(1 + offset_bytes + (kBlockSize / 2) * delta_nibbles);
+ S2_DCHECK_LE(offset_bytes - overlap_nibbles, 7);
+ S2_DCHECK_LE(overlap_nibbles, 1);
+ S2_DCHECK_LE(delta_nibbles, 16);
+ block->put8((offset_bytes - overlap_nibbles) |
+ (overlap_nibbles << 3) | (delta_nibbles - 1) << 4);
+
+ // Determine the offset for this block, and whether there are exceptions.
+ uint64 offset = ~0ULL;
+ int num_exceptions = 0;
+ for (int j = 0; j < block_size; ++j) {
+ if (values[i + j] == kException) {
+ num_exceptions += 1;
+ } else {
+ S2_DCHECK_GE(values[i + j], base);
+ offset = min(offset, values[i + j] - base);
+ }
+ }
+ if (num_exceptions == block_size) offset = 0;
+
+ // Encode the offset.
+ int offset_shift = code.delta_bits - code.overlap_bits;
+ offset &= ~BitMask(offset_shift);
+ S2_DCHECK_EQ(offset == 0, offset_bytes == 0);
+ if (offset > 0) {
+ EncodeUintWithLength(offset >> offset_shift, offset_bytes, block);
+ }
+
+ // Encode the deltas, and also gather any exceptions present.
+ int delta_bytes = (delta_nibbles + 1) >> 1;
+ exceptions.clear();
+ for (int j = 0; j < block_size; ++j) {
+ uint64 delta;
+ if (values[i + j] == kException) {
+ delta = exceptions.size();
+ exceptions.push_back(points[i + j]);
+ } else {
+ S2_DCHECK_GE(values[i + j], offset + base);
+ delta = values[i + j] - (offset + base);
+ if (have_exceptions) {
+ S2_DCHECK_LE(delta, ~0ULL - kBlockSize);
+ delta += kBlockSize;
+ }
+ }
+ S2_DCHECK_LE(delta, BitMask(code.delta_bits));
+ if ((delta_nibbles & 1) && (j & 1)) {
+ // Combine this delta with the high-order 4 bits of the previous delta.
+ uint8 last_byte = *(block->base() + block->length() - 1);
+ block->RemoveLast(1);
+ delta = (delta << 4) | (last_byte & 0xf);
+ }
+ EncodeUintWithLength(delta, delta_bytes, block);
+ }
+ // Append any exceptions to the end of the block.
+ if (num_exceptions > 0) {
+ int exceptions_bytes = exceptions.size() * sizeof(S2Point);
+ block->Ensure(exceptions_bytes);
+ block->putn(exceptions.data(), exceptions_bytes);
+ }
+ offset_bytes_sum += offset_bytes;
+ delta_nibbles_sum += delta_nibbles;
+ exceptions_sum += num_exceptions;
+ }
+ blocks.Encode(encoder);
+}
+
+// Returns the S2CellId level for which the greatest number of the given points
+// can be represented as the center of an S2CellId. Initializes "cell_points"
+// to contain the S2CellId representation of each point (if any). Returns -1
+// if there is no S2CellId that would result in significant space savings.
+int ChooseBestLevel(Span<const S2Point> points,
+ vector<CellPoint>* cell_points) {
+ cell_points->clear();
+ cell_points->reserve(points.size());
+
+ // Count the number of points at each level.
+ int level_counts[S2CellId::kMaxLevel + 1] = { 0 };
+ for (const S2Point& point : points) {
+ int face;
+ uint32 si, ti;
+ int level = S2::XYZtoFaceSiTi(point, &face, &si, &ti);
+ cell_points->push_back(CellPoint(level, face, si, ti));
+ if (level >= 0) ++level_counts[level];
+ }
+ // Choose the level for which the most points can be encoded.
+ int best_level = 0;
+ for (int level = 1; level <= S2CellId::kMaxLevel; ++level) {
+ if (level_counts[level] > level_counts[best_level]) {
+ best_level = level;
+ }
+ }
+ // The uncompressed encoding is smaller *and* faster when very few of the
+ // points are encodable as S2CellIds. The CELL_IDS encoding uses about 1
+ // extra byte per point in this case, consisting of up to a 3 byte
+ // EncodedStringVector offset for each block, a 1 byte block header, and 4
+ // bits per delta (encoding an exception number from 0-7), for a total of 8
+ // bytes per block. This represents a space overhead of about 4%, so we
+ // require that at least 5% of the input points should be encodable as
+ // S2CellIds in order for the CELL_IDS format to be worthwhile.
+ constexpr double kMinEncodableFraction = 0.05;
+ if (level_counts[best_level] <= kMinEncodableFraction * points.size()) {
+ return -1;
+ }
+ return best_level;
+}
+
+// Given a vector of points in CellPoint format and an S2CellId level that has
+// been chosen for encoding, returns a vector of 64-bit values that should be
+// encoded in order to represent these points. Points that cannot be
+// represented losslessly as the center of an S2CellId at the chosen level are
+// indicated by the value "kException". "have_exceptions" is set to indicate
+// whether any exceptions were present.
+vector<uint64> ConvertCellsToValues(const vector<CellPoint>& cell_points,
+ int level, bool* have_exceptions) {
+ vector<uint64> values;
+ values.reserve(cell_points.size());
+ *have_exceptions = false;
+ int shift = S2CellId::kMaxLevel - level;
+ for (CellPoint cp : cell_points) {
+ if (cp.level != level) {
+ values.push_back(kException);
+ *have_exceptions = true;
+ } else {
+ // Note that bit 31 of tj is always zero, and that bits are interleaved in
+ // such a way that bit 63 of the result is always zero.
+ //
+ // The S2CellId version of the following code is:
+ // uint64 v = S2CellId::FromFaceIJ(cp.face, cp.si >> 1, cp.ti >> 1).
+ // parent(level).id() >> (2 * shift + 1);
+ uint32 sj = (((cp.face & 3) << 30) | (cp.si >> 1)) >> shift;
+ uint32 tj = (((cp.face & 4) << 29) | cp.ti) >> (shift + 1);
+ uint64 v = InterleaveUint32BitPairs(sj, tj);
+ S2_DCHECK_LE(v, BitMask(MaxBitsForLevel(level)));
+ values.push_back(v);
+ }
+ }
+ return values;
+}
+
+uint64 ChooseBase(const vector<uint64>& values, int level, bool have_exceptions,
+ int* base_bits) {
+ // Find the minimum and maximum non-exception values to be represented.
+ uint64 v_min = kException, v_max = 0;
+ for (auto v : values) {
+ if (v != kException) {
+ v_min = min(v_min, v);
+ v_max = max(v_max, v);
+ }
+ }
+ if (v_min == kException) return 0;
+
+ // Generally "base" is chosen as the bit prefix shared by v_min and v_max.
+ // However there are a few adjustments we need to make.
+ //
+ // 1. Encodings are usually smaller if the bits represented by "base" and
+ // "delta" do not overlap. Usually the shared prefix rule does this
+ // automatically, but if v_min == v_max or there are special circumstances
+ // that increase delta_bits (such as values.size() == 1) then we need to
+ // make an adjustment.
+ //
+ // 2. The format only allows us to represent up to 7 bytes (56 bits) of
+ // "base", so we need to ensure that "base" conforms to this requirement.
+ int min_delta_bits = (have_exceptions || values.size() == 1) ? 8 : 4;
+ int excluded_bits = max(Bits::Log2Floor64(v_min ^ v_max) + 1,
+ max(min_delta_bits, BaseShift(level, 56)));
+ uint64 base = v_min & ~BitMask(excluded_bits);
+
+ // Determine how many bytes are needed to represent this prefix.
+ if (base == 0) {
+ *base_bits = 0;
+ } else {
+ int low_bit = Bits::FindLSBSetNonZero64(base);
+ *base_bits = (MaxBitsForLevel(level) - low_bit + 7) & ~7;
+ }
+
+ // Since base_bits has been rounded up to a multiple of 8, we may now be
+ // able to represent additional bits of v_min. In general this reduces the
+ // final encoded size.
+ //
+ // NOTE(ericv): A different strategy for choosing "base" is to encode all
+ // blocks under the assumption that "base" equals v_min exactly, and then
+ // set base equal to the minimum-length prefix of "v_min" that allows these
+ // encodings to be used. This strategy reduces the encoded sizes by
+ // about 0.2% relative to the strategy here, but is more complicated.
+ return v_min & ~BitMask(BaseShift(level, *base_bits));
+}
+
+// Returns true if the range of values [d_min, d_max] can be encoded using the
+// specified parameters (delta_bits, overlap_bits, and have_exceptions).
+bool CanEncode(uint64 d_min, uint64 d_max, int delta_bits,
+ int overlap_bits, bool have_exceptions) {
+ // "offset" can't represent the lowest (delta_bits - overlap_bits) of d_min.
+ d_min &= ~BitMask(delta_bits - overlap_bits);
+
+ // The maximum delta is reduced by kBlockSize if any exceptions exist, since
+ // deltas 0..kBlockSize-1 are used to indicate exceptions.
+ uint64 max_delta = BitMask(delta_bits);
+ if (have_exceptions) {
+ if (max_delta < kBlockSize) return false;
+ max_delta -= kBlockSize;
+ }
+ // The first test below is necessary to avoid 64-bit overflow.
+ return (d_min > ~max_delta) || (d_min + max_delta >= d_max);
+}
+
+// Given a vector of 64-bit values to be encoded and an S2CellId level, returns
+// the optimal encoding parameters that should be used to encode each block.
+// Also returns the global minimum value "base" and the number of bits that
+// should be used to encode it ("base_bits").
+BlockCode GetBlockCode(Span<const uint64> values, uint64 base,
+ bool have_exceptions) {
+ // "b_min" and "b_max"n are the minimum and maximum values within this block.
+ uint64 b_min = kException, b_max = 0;
+ for (uint64 v : values) {
+ if (v != kException) {
+ b_min = min(b_min, v);
+ b_max = max(b_max, v);
+ }
+ }
+ if (b_min == kException) {
+ // All values in this block are exceptions.
+ return BlockCode{4, 0, 0};
+ }
+
+ // Adjust the min/max values so that they are relative to "base".
+ b_min -= base;
+ b_max -= base;
+
+ // Determine the minimum possible delta length and overlap that can be used
+ // to encode this block. The block will usually be encodable using the
+ // number of bits in (b_max - b_min) rounded up to a multiple of 4. If this
+ // is not possible, the preferred solution is to shift "offset" so that the
+ // delta and offset values overlap by 4 bits (since this only costs an
+ // average of 4 extra bits per block). Otherwise we increase the delta size
+ // by 4 bits. Certain cases require that both of these techniques are used.
+ //
+ // Example 1: b_min = 0x72, b_max = 0x7e. The range is 0x0c. This can be
+ // encoded using delta_bits = 4 and overlap_bits = 0, which allows us to
+ // represent an offset of 0x70 and a maximum delta of 0x0f, so that we can
+ // encode values up to 0x7f.
+ //
+ // Example 2: b_min = 0x78, b_max = 0x84. The range is 0x0c, but in this
+ // case it is not sufficient to use delta_bits = 4 and overlap_bits = 0
+ // because we can again only represent an offset of 0x70, so the maximum
+ // delta of 0x0f only lets us encode values up to 0x7f. However if we
+ // increase the overlap to 4 bits then we can represent an offset of 0x78,
+ // which lets us encode values up to 0x78 + 0x0f = 0x87.
+ //
+ // Example 3: b_min = 0x08, b_max = 0x104. The range is 0xfc, so we should
+ // be able to use 8-bit deltas. But even with a 4-bit overlap, we can still
+ // only encode offset = 0 and a maximum value of 0xff. (We don't allow
+ // bigger overlaps because statistically they are not worthwhile.) Instead
+ // we increase the delta size to 12 bits, which handles this case easily.
+ //
+ // Example 4: b_min = 0xf08, b_max = 0x1004. The range is 0xfc, so we
+ // should be able to use 8-bit deltas. With 8-bit deltas and no overlap, we
+ // have offset = 0xf00 and a maximum encodable value of 0xfff. With 8-bit
+ // deltas and a 4-bit overlap, we still have offset = 0xf00 and a maximum
+ // encodable value of 0xfff. Even with 12-bit deltas, we have offset = 0
+ // and we can still only represent 0xfff. However with delta_bits = 12 and
+ // overlap_bits = 4, we can represent offset = 0xf00 and a maximum encodable
+ // value of 0xf00 + 0xfff = 0x1eff.
+ //
+ // It is possible to show that this last example is the worst case, i.e. we
+ // do not need to consider increasing delta_bits or overlap_bits further.
+ int delta_bits = (max(1, Bits::Log2Floor64(b_max - b_min)) + 3) & ~3;
+ int overlap_bits = 0;
+ if (!CanEncode(b_min, b_max, delta_bits, 0, have_exceptions)) {
+ if (CanEncode(b_min, b_max, delta_bits, 4, have_exceptions)) {
+ overlap_bits = 4;
+ } else {
+ S2_DCHECK_LE(delta_bits, 60);
+ delta_bits += 4;
+ if (!CanEncode(b_min, b_max, delta_bits, 0, have_exceptions)) {
+ S2_DCHECK(CanEncode(b_min, b_max, delta_bits, 4, have_exceptions));
+ overlap_bits = 4;
+ }
+ }
+ }
+
+ // Avoid wasting 4 bits of delta when the block size is 1. This reduces the
+ // encoding size for single leaf cells by one byte.
+ if (values.size() == 1) {
+ S2_DCHECK(delta_bits == 4 && overlap_bits == 0);
+ delta_bits = 8;
+ }
+
+ // Now determine the number of bytes needed to encode "offset", given the
+ // chosen delta length.
+ uint64 max_delta = BitMask(delta_bits) - (have_exceptions ? kBlockSize : 0);
+ int offset_bits = 0;
+ if (b_max > max_delta) {
+ // At least one byte of offset is required. Round up the minimum offset
+ // to the next encodable value, and determine how many bits it has.
+ int offset_shift = delta_bits - overlap_bits;
+ uint64 mask = BitMask(offset_shift);
+ uint64 min_offset = (b_max - max_delta + mask) & ~mask;
+ S2_DCHECK_GT(min_offset, 0);
+ offset_bits =
+ (Bits::FindMSBSetNonZero64(min_offset) + 1 - offset_shift + 7) & ~7;
+ // A 64-bit offset can only be encoded with an overlap of 4 bits.
+ if (offset_bits == 64) overlap_bits = 4;
+ }
+ return BlockCode{delta_bits, offset_bits, overlap_bits};
+}
+
+bool EncodedS2PointVector::InitCellIdsFormat(Decoder* decoder) {
+ // This function inverts the encodings documented above.
+ // First we decode the two-byte header.
+ if (decoder->avail() < 2) return false;
+ uint8 header1 = decoder->get8();
+ uint8 header2 = decoder->get8();
+ S2_DCHECK_EQ(header1 & 7, CELL_IDS);
+ int last_block_count, base_bytes;
+ cell_ids_.have_exceptions = (header1 & 8) != 0;
+ last_block_count = (header1 >> 4) + 1;
+ base_bytes = header2 & 7;
+ cell_ids_.level = header2 >> 3;
+
+ // Decode the base value (if any).
+ uint64 base;
+ if (!DecodeUintWithLength(base_bytes, decoder, &base)) return false;
+ cell_ids_.base = base << BaseShift(cell_ids_.level, base_bytes << 3);
+
+ // Initialize the vector of encoded blocks.
+ if (!cell_ids_.blocks.Init(decoder)) return false;
+ size_ = kBlockSize * (cell_ids_.blocks.size() - 1) + last_block_count;
+ return true;
+}
+
+S2Point EncodedS2PointVector::DecodeCellIdsFormat(int i) const {
+ // This function inverts the encodings documented above.
+
+ // First we decode the block header.
+ const char* ptr = cell_ids_.blocks.GetStart(i >> kBlockShift);
+ uint8 header = *ptr++;
+ int overlap_nibbles = (header >> 3) & 1;
+ int offset_bytes = (header & 7) + overlap_nibbles;
+ int delta_nibbles = (header >> 4) + 1;
+
+ // Decode the offset for this block.
+ int offset_shift = (delta_nibbles - overlap_nibbles) << 2;
+ uint64 offset = GetUintWithLength<uint64>(ptr, offset_bytes) << offset_shift;
+ ptr += offset_bytes;
+
+ // Decode the delta for the requested value.
+ int delta_nibble_offset = (i & (kBlockSize - 1)) * delta_nibbles;
+ int delta_bytes = (delta_nibbles + 1) >> 1;
+ const char* delta_ptr = ptr + (delta_nibble_offset >> 1);
+ uint64 delta = GetUintWithLength<uint64>(delta_ptr, delta_bytes);
+ delta >>= (delta_nibble_offset & 1) << 2;
+ delta &= BitMask(delta_nibbles << 2);
+
+ // Test whether this point is encoded as an exception.
+ if (cell_ids_.have_exceptions) {
+ if (delta < kBlockSize) {
+ int block_size = min(kBlockSize, size_ - (i & ~(kBlockSize - 1)));
+ ptr += (block_size * delta_nibbles + 1) >> 1;
+ ptr += delta * sizeof(S2Point);
+ return *reinterpret_cast<const S2Point*>(ptr);
+ }
+ delta -= kBlockSize;
+ }
+
+ // Otherwise convert the 64-bit value back to an S2Point.
+ uint64 value = cell_ids_.base + offset + delta;
+ int shift = S2CellId::kMaxLevel - cell_ids_.level;
+
+ // The S2CellId version of the following code is:
+ // return S2CellId(((value << 1) | 1) << (2 * shift)).ToPoint();
+ uint32 sj, tj;
+ DeinterleaveUint32BitPairs(value, &sj, &tj);
+ int si = (((sj << 1) | 1) << shift) & 0x7fffffff;
+ int ti = (((tj << 1) | 1) << shift) & 0x7fffffff;
+ int face = ((sj << shift) >> 30) | (((tj << (shift + 1)) >> 29) & 4);
+ return S2::FaceUVtoXYZ(face, S2::STtoUV(S2::SiTitoST(si)),
+ S2::STtoUV(S2::SiTitoST(ti))).Normalize();
+}
+
+} // namespace s2coding
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/encoded_s2shape_index.h"
+
+#include <memory>
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/mutable_s2shape_index.h"
+
+using absl::make_unique;
+using std::unique_ptr;
+using std::vector;
+
+bool EncodedS2ShapeIndex::Iterator::Locate(const S2Point& target) {
+ return LocateImpl(target, this);
+}
+
+EncodedS2ShapeIndex::CellRelation EncodedS2ShapeIndex::Iterator::Locate(
+ S2CellId target) {
+ return LocateImpl(target, this);
+}
+
+unique_ptr<EncodedS2ShapeIndex::IteratorBase>
+EncodedS2ShapeIndex::Iterator::Clone() const {
+ return make_unique<Iterator>(*this);
+}
+
+void EncodedS2ShapeIndex::Iterator::Copy(const IteratorBase& other) {
+ *this = *down_cast<const Iterator*>(&other);
+}
+
+S2Shape* EncodedS2ShapeIndex::GetShape(int id) const {
+ // This method is called when a shape has not been decoded yet.
+ unique_ptr<S2Shape> shape = (*shape_factory_)[id];
+ if (shape) shape->id_ = id;
+ S2Shape* expected = kUndecodedShape();
+ if (shapes_[id].compare_exchange_strong(expected, shape.get(),
+ std::memory_order_relaxed)) {
+ return shape.release(); // Ownership has been transferred to shapes_.
+ }
+ return shapes_[id].load(std::memory_order_relaxed);
+}
+
+inline const S2ShapeIndexCell* EncodedS2ShapeIndex::GetCell(int i) const {
+ if (cell_decoded(i)) {
+ auto cell = cells_[i].load(std::memory_order_acquire);
+ if (cell != nullptr) return cell;
+ }
+ // We decode the cell before acquiring the spinlock in order to minimize the
+ // time that the lock is held.
+ auto cell = make_unique<S2ShapeIndexCell>();
+ Decoder decoder = encoded_cells_.GetDecoder(i);
+ if (!cell->Decode(num_shape_ids(), &decoder)) {
+ return nullptr;
+ }
+ SpinLockHolder l(&cells_lock_);
+ if (test_and_set_cell_decoded(i)) {
+ // This cell has already been decoded.
+ return cells_[i].load(std::memory_order_relaxed);
+ }
+ if (cell_cache_.size() < max_cell_cache_size()) {
+ cell_cache_.push_back(i);
+ }
+ cells_[i].store(cell.get(), std::memory_order_relaxed);
+ return cell.release(); // Ownership has been transferred to cells_.
+}
+
+const S2ShapeIndexCell* EncodedS2ShapeIndex::Iterator::GetCell() const {
+ return index_->GetCell(cell_pos_);
+}
+
+EncodedS2ShapeIndex::EncodedS2ShapeIndex() {
+}
+
+EncodedS2ShapeIndex::~EncodedS2ShapeIndex() {
+ // Although Minimize() does slightly more than required for destruction
+ // (i.e., it resets vector elements to their default values), this does not
+ // affect benchmark times.
+ Minimize();
+}
+
+bool EncodedS2ShapeIndex::Init(Decoder* decoder,
+ const ShapeFactory& shape_factory) {
+ Minimize();
+ uint64 max_edges_version;
+ if (!decoder->get_varint64(&max_edges_version)) return false;
+ int version = max_edges_version & 3;
+ if (version != MutableS2ShapeIndex::kCurrentEncodingVersionNumber) {
+ return false;
+ }
+ options_.set_max_edges_per_cell(max_edges_version >> 2);
+
+ // AtomicShape is a subtype of std::atomic<S2Shape*> that changes the
+ // default constructor value to kUndecodedShape(). This saves the effort of
+ // initializing all the elements twice.
+ shapes_ = std::vector<AtomicShape>(shape_factory.size());
+ shape_factory_ = shape_factory.Clone();
+ if (!cell_ids_.Init(decoder)) return false;
+
+ // The cells_ elements are *uninitialized memory*. Instead we have bit
+ // vector (cells_decoded_) to indicate which elements of cells_ are valid.
+ // This reduces constructor times by more than a factor of 50, since rather
+ // than needing to initialize one 64-bit pointer per cell to zero, we only
+ // need to initialize one bit per cell to zero.
+ //
+ // For very large S2ShapeIndexes the internal memset() call to initialize
+ // cells_decoded_ still takes about 4 microseconds per million cells, but
+ // this seems reasonable relative to other likely costs (I/O, etc).
+ //
+ // NOTE(ericv): DO NOT use make_unique<> here! make_unique<> allocates memory
+ // using "new T[n]()", which initializes all elements of the array. This
+ // slows down some benchmarks by over 100x.
+ //
+ // cells_ = make_unique<std::atomic<S2ShapeIndexCell*>[]>(cell_ids_.size());
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ // NO NO NO
+ cells_.reset(new std::atomic<S2ShapeIndexCell*>[cell_ids_.size()]);
+ cells_decoded_ = vector<std::atomic<uint64>>((cell_ids_.size() + 63) >> 6);
+
+ return encoded_cells_.Init(decoder);
+}
+
+void EncodedS2ShapeIndex::Minimize() {
+ if (cells_ == nullptr) return; // Not initialized yet.
+
+ for (auto& atomic_shape : shapes_) {
+ S2Shape* shape = atomic_shape.load(std::memory_order_relaxed);
+ if (shape != kUndecodedShape() && shape != nullptr) {
+ atomic_shape.store(kUndecodedShape(), std::memory_order_relaxed);
+ delete shape;
+ }
+ }
+ if (cell_cache_.size() < max_cell_cache_size()) {
+ // When only a tiny fraction of the cells are decoded, we keep track of
+ // those cells in cell_cache_ to avoid the cost of scanning the
+ // cells_decoded_ vector. (The cost is only about 1 cycle per 64 cells,
+ // but for a huge polygon with 1 million cells that's still 16000 cycles.)
+ for (int pos : cell_cache_) {
+ cells_decoded_[pos >> 6].store(0, std::memory_order_relaxed);
+ delete cells_[pos].load(std::memory_order_relaxed);
+ }
+ } else {
+ // Scan the cells_decoded_ vector looking for cells that must be deleted.
+ for (int i = cells_decoded_.size(), base = 0; --i >= 0; base += 64) {
+ uint64 bits = cells_decoded_[i].load(std::memory_order_relaxed);
+ if (bits == 0) continue;
+ do {
+ int offset = Bits::FindLSBSetNonZero64(bits);
+ delete cells_[(i << 6) + offset].load(std::memory_order_relaxed);
+ bits &= bits - 1;
+ } while (bits != 0);
+ cells_decoded_[i].store(0, std::memory_order_relaxed);
+ }
+ }
+ cell_cache_.clear();
+}
+
+size_t EncodedS2ShapeIndex::SpaceUsed() const {
+ // TODO(ericv): Add SpaceUsed() method to S2Shape base class,and Include
+ // memory owned by the allocated S2Shapes (here and in S2ShapeIndex).
+ size_t size = sizeof(*this);
+ size += shapes_.capacity() * sizeof(std::atomic<S2Shape*>);
+ size += cell_ids_.size() * sizeof(std::atomic<S2ShapeIndexCell*>); // cells_
+ size += cells_decoded_.capacity() * sizeof(std::atomic<uint64>);
+ size += cell_cache_.capacity() * sizeof(int);
+ return size;
+}
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/encoded_string_vector.h"
+
+using absl::MakeSpan;
+using absl::Span;
+using absl::string_view;
+using std::vector;
+
+namespace s2coding {
+
+StringVectorEncoder::StringVectorEncoder() {
+}
+
+void StringVectorEncoder::Encode(Encoder* encoder) {
+ offsets_.push_back(data_.length());
+ // We don't encode the first element of "offsets_", which is always zero.
+ EncodeUintVector<uint64>(
+ MakeSpan(offsets_.data() + 1, offsets_.data() + offsets_.size()),
+ encoder);
+ encoder->Ensure(data_.length());
+ encoder->putn(data_.base(), data_.length());
+}
+
+void StringVectorEncoder::Encode(Span<const string> v, Encoder* encoder) {
+ StringVectorEncoder string_vector;
+ for (const auto& str : v) string_vector.Add(str);
+ string_vector.Encode(encoder);
+}
+
+bool EncodedStringVector::Init(Decoder* decoder) {
+ if (!offsets_.Init(decoder)) return false;
+ data_ = reinterpret_cast<const char*>(decoder->ptr());
+ if (offsets_.size() > 0) {
+ uint64 length = offsets_[offsets_.size() - 1];
+ if (decoder->avail() < length) return false;
+ decoder->skip(length);
+ }
+ return true;
+}
+
+vector<string_view> EncodedStringVector::Decode() const {
+ size_t n = size();
+ vector<string_view> result(n);
+ for (int i = 0; i < n; ++i) {
+ result[i] = (*this)[i];
+ }
+ return result;
+}
+
+} // namespace s2coding
--- /dev/null
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/id_set_lexicon.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "s2/base/logging.h"
+
+IdSetLexicon::IdSetLexicon() {
+}
+
+IdSetLexicon::~IdSetLexicon() {
+}
+
+// We define the copy/move constructors and assignment operators explicitly
+// in order to avoid copying/moving the temporary storage vector "tmp_".
+
+IdSetLexicon::IdSetLexicon(const IdSetLexicon& x) : id_sets_(x.id_sets_) {
+}
+
+IdSetLexicon::IdSetLexicon(IdSetLexicon&& x) : id_sets_(std::move(x.id_sets_)) {
+}
+
+IdSetLexicon& IdSetLexicon::operator=(const IdSetLexicon& x) {
+ id_sets_ = x.id_sets_;
+ return *this;
+}
+
+IdSetLexicon& IdSetLexicon::operator=(IdSetLexicon&& x) {
+ id_sets_ = std::move(x.id_sets_);
+ return *this;
+}
+
+void IdSetLexicon::Clear() {
+ id_sets_.Clear();
+}
+
+int32 IdSetLexicon::AddInternal(std::vector<int32>* ids) {
+ if (ids->empty()) {
+ // Empty sets have a special id chosen not to conflict with other ids.
+ return kEmptySetId;
+ } else if (ids->size() == 1) {
+ // Singleton sets are represented by their element.
+ return (*ids)[0];
+ } else {
+ // Canonicalize the set by sorting and removing duplicates.
+ std::sort(ids->begin(), ids->end());
+ ids->erase(std::unique(ids->begin(), ids->end()), ids->end());
+ // Non-singleton sets are represented by the bitwise complement of the id
+ // returned by SequenceLexicon.
+ return ~id_sets_.Add(*ids);
+ }
+}
+
+IdSetLexicon::IdSet IdSetLexicon::id_set(int32 set_id) const {
+ if (set_id >= 0) {
+ return IdSet(set_id);
+ } else if (set_id == kEmptySetId) {
+ return IdSet();
+ } else {
+ auto sequence = id_sets_.sequence(~set_id);
+ S2_DCHECK_NE(0, sequence.size());
+ return IdSet(&*sequence.begin(), &*sequence.begin() + sequence.size());
+ }
+}
--- /dev/null
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/mutable_s2shape_index.h"
+
+#include <algorithm>
+#include <atomic>
+#include <cmath>
+
+#include "s2/base/casts.h"
+#include "s2/base/commandlineflags.h"
+#include "s2/base/spinlock.h"
+#include "s2/encoded_s2cell_id_vector.h"
+#include "s2/encoded_string_vector.h"
+#include "s2/r1interval.h"
+#include "s2/r2.h"
+#include "s2/r2rect.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2cell_union.h"
+#include "s2/s2coords.h"
+#include "s2/s2edge_clipping.h"
+#include "s2/s2edge_crosser.h"
+#include "s2/s2metrics.h"
+#include "s2/s2padded_cell.h"
+#include "s2/s2pointutil.h"
+#include "s2/s2shapeutil_contains_brute_force.h"
+
+using std::fabs;
+using std::max;
+using std::unique_ptr;
+using std::vector;
+
+// FLAGS_s2shape_index_default_max_edges_per_cell
+//
+// The default maximum number of edges per cell (not counting "long" edges).
+// If a cell has more than this many edges, and it is not a leaf cell, then it
+// is subdivided. This flag can be overridden via MutableS2ShapeIndex::Options.
+// Reasonable values range from 10 to about 50 or so.
+DEFINE_int32(
+ s2shape_index_default_max_edges_per_cell, 10,
+ "Default maximum number of edges (not counting 'long' edges) per cell; "
+ "reasonable values range from 10 to 50. Small values makes queries "
+ "faster, while large values make construction faster and use less "
+ "memory.");
+
+// FLAGS_s2shape_index_tmp_memory_budget_mb
+//
+// Attempt to limit the amount of temporary memory allocated while building or
+// updating a MutableS2ShapeIndex to at most this value. This is achieved by
+// splitting the updates into multiple batches when necessary. (The memory
+// required is proportional to the number of edges being updated at once.)
+//
+// Note that this limit is not a hard guarantee, for several reasons:
+// (1) the memory estimates are only approximations;
+// (2) all edges in a given shape are added or removed at once, so shapes
+// with huge numbers of edges may exceed the budget;
+// (3) shapes being removed are always processed in a single batch. (This
+// could be fixed, but it seems better to keep the code simpler for now.)
+DEFINE_int32(
+ s2shape_index_tmp_memory_budget_mb, 100,
+ "Attempts to limit the amount of temporary memory used by "
+ "MutableS2ShapeIndex when creating or updating very large indexes "
+ "to at most this value. If more memory than this is needed, updates "
+ "will automatically be split into batches internally.");
+
+// FLAGS_s2shape_index_cell_size_to_long_edge_ratio
+//
+// The cell size relative to the length of an edge at which it is first
+// considered to be "long". Long edges do not contribute toward the decision
+// to subdivide a cell further. For example, a value of 2.0 means that the
+// cell must be at least twice the size of the edge in order for that edge to
+// be counted. There are two reasons for not counting long edges: (1) such
+// edges typically need to be propagated to several children, which increases
+// time and memory costs without much benefit, and (2) in pathological cases,
+// many long edges close together could force subdivision to continue all the
+// way to the leaf cell level.
+DEFINE_double(
+ s2shape_index_cell_size_to_long_edge_ratio, 1.0,
+ "The cell size relative to the length of an edge at which it is first "
+ "considered to be 'long'. Long edges do not contribute to the decision "
+ "to subdivide a cell further. The size and speed of the index are "
+ "typically not very sensitive to this parameter. Reasonable values range "
+ "from 0.1 to 10, with smaller values causing more aggressive subdivision "
+ "of long edges grouped closely together.");
+
+// The total error when clipping an edge comes from two sources:
+// (1) Clipping the original spherical edge to a cube face (the "face edge").
+// The maximum error in this step is S2::kFaceClipErrorUVCoord.
+// (2) Clipping the face edge to the u- or v-coordinate of a cell boundary.
+// The maximum error in this step is S2::kEdgeClipErrorUVCoord.
+// Finally, since we encounter the same errors when clipping query edges, we
+// double the total error so that we only need to pad edges during indexing
+// and not at query time.
+const double MutableS2ShapeIndex::kCellPadding =
+ 2 * (S2::kFaceClipErrorUVCoord + S2::kEdgeClipErrorUVCoord);
+
+MutableS2ShapeIndex::Options::Options()
+ : max_edges_per_cell_(FLAGS_s2shape_index_default_max_edges_per_cell) {
+}
+
+void MutableS2ShapeIndex::Options::set_max_edges_per_cell(
+ int max_edges_per_cell) {
+ max_edges_per_cell_ = max_edges_per_cell;
+}
+
+bool MutableS2ShapeIndex::Iterator::Locate(const S2Point& target) {
+ return LocateImpl(target, this);
+}
+
+MutableS2ShapeIndex::CellRelation MutableS2ShapeIndex::Iterator::Locate(
+ S2CellId target) {
+ return LocateImpl(target, this);
+}
+
+const S2ShapeIndexCell* MutableS2ShapeIndex::Iterator::GetCell() const {
+ S2_LOG(DFATAL) << "Should never be called";
+ return nullptr;
+}
+
+unique_ptr<MutableS2ShapeIndex::IteratorBase>
+MutableS2ShapeIndex::Iterator::Clone() const {
+ return absl::make_unique<Iterator>(*this);
+}
+
+void MutableS2ShapeIndex::Iterator::Copy(const IteratorBase& other) {
+ *this = *down_cast<const Iterator*>(&other);
+}
+
+// Defines the initial focus point of MutableS2ShapeIndex::InteriorTracker
+// (the start of the S2CellId space-filling curve).
+//
+// TODO(ericv): Move InteriorTracker here to avoid the need for this method.
+static S2Point kInteriorTrackerOrigin() {
+ return S2::FaceUVtoXYZ(0, -1, -1).Normalize();
+}
+
+MutableS2ShapeIndex::MutableS2ShapeIndex()
+ : index_status_(FRESH) {
+}
+
+MutableS2ShapeIndex::MutableS2ShapeIndex(const Options& options)
+ : options_(options),
+ index_status_(FRESH) {
+}
+
+void MutableS2ShapeIndex::Init(const Options& options) {
+ S2_DCHECK(shapes_.empty());
+ options_ = options;
+}
+
+MutableS2ShapeIndex::~MutableS2ShapeIndex() {
+ Clear();
+}
+
+void MutableS2ShapeIndex::Minimize() {
+ // TODO(ericv): Implement. In theory we should be able to discard the
+ // entire index and rebuild it the next time it is needed.
+}
+
+int MutableS2ShapeIndex::Add(unique_ptr<S2Shape> shape) {
+ // Additions are processed lazily by ApplyUpdates().
+ const int id = shapes_.size();
+ shape->id_ = id;
+ shapes_.push_back(std::move(shape));
+ index_status_.store(STALE, std::memory_order_relaxed);
+ return id;
+}
+
+unique_ptr<S2Shape> MutableS2ShapeIndex::Release(int shape_id) {
+ // This class updates itself lazily, because it is much more efficient to
+ // process additions and removals in batches. However this means that when
+ // a shape is removed, we need to make a copy of all its edges, since the
+ // client is free to delete "shape" once this call is finished.
+
+ S2_DCHECK(shapes_[shape_id] != nullptr);
+ auto shape = std::move(shapes_[shape_id]);
+ if (shape_id >= pending_additions_begin_) {
+ // We are removing a shape that has not yet been added to the index,
+ // so there is nothing else to do.
+ } else {
+ if (!pending_removals_) {
+ pending_removals_.reset(new vector<RemovedShape>);
+ }
+ // We build the new RemovedShape in place, since it includes a potentially
+ // large vector of edges that might be expensive to copy.
+ pending_removals_->push_back(RemovedShape());
+ RemovedShape* removed = &pending_removals_->back();
+ removed->shape_id = shape->id();
+ removed->has_interior = (shape->dimension() == 2);
+ removed->contains_tracker_origin =
+ s2shapeutil::ContainsBruteForce(*shape, kInteriorTrackerOrigin());
+ int num_edges = shape->num_edges();
+ removed->edges.reserve(num_edges);
+ for (int e = 0; e < num_edges; ++e) {
+ removed->edges.push_back(shape->edge(e));
+ }
+ }
+ index_status_.store(STALE, std::memory_order_relaxed);
+ return shape;
+}
+
+vector<unique_ptr<S2Shape>> MutableS2ShapeIndex::ReleaseAll() {
+ Iterator it;
+ for (it.InitStale(this, S2ShapeIndex::BEGIN); !it.done(); it.Next()) {
+ delete &it.cell();
+ }
+ cell_map_.clear();
+ pending_additions_begin_ = 0;
+ pending_removals_.reset();
+ S2_DCHECK(update_state_ == nullptr);
+ index_status_.store(FRESH, std::memory_order_relaxed);
+ vector<unique_ptr<S2Shape>> result;
+ result.swap(shapes_);
+ return result;
+}
+
+void MutableS2ShapeIndex::Clear() {
+ ReleaseAll();
+}
+
+// FaceEdge and ClippedEdge store temporary edge data while the index is being
+// updated. FaceEdge represents an edge that has been projected onto a given
+// face, while ClippedEdge represents the portion of that edge that has been
+// clipped to a given S2Cell.
+//
+// While it would be possible to combine all the edge information into one
+// structure, there are two good reasons for separating it:
+//
+// - Memory usage. Separating the two classes means that we only need to
+// store one copy of the per-face data no matter how many times an edge is
+// subdivided, and it also lets us delay computing bounding boxes until
+// they are needed for processing each face (when the dataset spans
+// multiple faces).
+//
+// - Performance. UpdateEdges is significantly faster on large polygons when
+// the data is separated, because it often only needs to access the data in
+// ClippedEdge and this data is cached more successfully.
+
+struct MutableS2ShapeIndex::FaceEdge {
+ int32 shape_id; // The shape that this edge belongs to
+ int32 edge_id; // Edge id within that shape
+ int32 max_level; // Not desirable to subdivide this edge beyond this level
+ bool has_interior; // Belongs to a shape of dimension 2.
+ R2Point a, b; // The edge endpoints, clipped to a given face
+ S2Shape::Edge edge; // The edge endpoints
+};
+
+struct MutableS2ShapeIndex::ClippedEdge {
+ const FaceEdge* face_edge; // The original unclipped edge
+ R2Rect bound; // Bounding box for the clipped portion
+};
+
+// Given a set of shapes, InteriorTracker keeps track of which shapes contain
+// a particular point (the "focus"). It provides an efficient way to move the
+// focus from one point to another and incrementally update the set of shapes
+// which contain it. We use this to compute which shapes contain the center
+// of every S2CellId in the index, by advancing the focus from one cell center
+// to the next.
+//
+// Initially the focus is at the start of the S2CellId space-filling curve.
+// We then visit all the cells that are being added to the MutableS2ShapeIndex
+// in increasing order of S2CellId. For each cell, we draw two edges: one
+// from the entry vertex to the center, and another from the center to the
+// exit vertex (where "entry" and "exit" refer to the points where the
+// space-filling curve enters and exits the cell). By counting edge crossings
+// we can incrementally compute which shapes contain the cell center. Note
+// that the same set of shapes will always contain the exit point of one cell
+// and the entry point of the next cell in the index, because either (a) these
+// two points are actually the same, or (b) the intervening cells in S2CellId
+// order are all empty, and therefore there are no edge crossings if we follow
+// this path from one cell to the other.
+class MutableS2ShapeIndex::InteriorTracker {
+ public:
+ // Constructs the InteriorTracker. You must call AddShape() for each shape
+ // that will be tracked before calling MoveTo() or DrawTo().
+ InteriorTracker();
+
+ // Returns the initial focus point when the InteriorTracker is constructed
+ // (corresponding to the start of the S2CellId space-filling curve).
+ static S2Point Origin();
+
+ // Returns the current focus point (see above).
+ const S2Point& focus() { return b_; }
+
+ // Returns true if any shapes are being tracked.
+ bool is_active() const { return is_active_; }
+
+ // Adds a shape whose interior should be tracked. "is_inside" indicates
+ // whether the current focus point is inside the shape. Alternatively, if
+ // the focus point is in the process of being moved (via MoveTo/DrawTo), you
+ // can also specify "is_inside" at the old focus point and call TestEdge()
+ // for every edge of the shape that might cross the current DrawTo() line.
+ // This updates the state to correspond to the new focus point.
+ //
+ // REQUIRES: shape->dimension() == 2
+ void AddShape(int32 shape_id, bool is_inside);
+
+ // Moves the focus to the given point. This method should only be used when
+ // it is known that there are no edge crossings between the old and new
+ // focus locations; otherwise use DrawTo().
+ void MoveTo(const S2Point& b) { b_ = b; }
+
+ // Moves the focus to the given point. After this method is called,
+ // TestEdge() should be called with all edges that may cross the line
+ // segment between the old and new focus locations.
+ void DrawTo(const S2Point& b);
+
+ // Indicates that the given edge of the given shape may cross the line
+ // segment between the old and new focus locations (see DrawTo).
+ // REQUIRES: shape->dimension() == 2
+ inline void TestEdge(int32 shape_id, const S2Shape::Edge& edge);
+
+ // The set of shape ids that contain the current focus.
+ const ShapeIdSet& shape_ids() const { return shape_ids_; }
+
+ // Indicates that the last argument to MoveTo() or DrawTo() was the entry
+ // vertex of the given S2CellId, i.e. the tracker is positioned at the start
+ // of this cell. By using this method together with at_cellid(), the caller
+ // can avoid calling MoveTo() in cases where the exit vertex of the previous
+ // cell is the same as the entry vertex of the current cell.
+ void set_next_cellid(S2CellId next_cellid) {
+ next_cellid_ = next_cellid.range_min();
+ }
+
+ // Returns true if the focus is already at the entry vertex of the given
+ // S2CellId (provided that the caller calls set_next_cellid() as each cell
+ // is processed).
+ bool at_cellid(S2CellId cellid) const {
+ return cellid.range_min() == next_cellid_;
+ }
+
+ // Makes an internal copy of the state for shape ids below the given limit,
+ // and then clear the state for those shapes. This is used during
+ // incremental updates to track the state of added and removed shapes
+ // separately.
+ void SaveAndClearStateBefore(int32 limit_shape_id);
+
+ // Restores the state previously saved by SaveAndClearStateBefore(). This
+ // only affects the state for shape_ids below "limit_shape_id".
+ void RestoreStateBefore(int32 limit_shape_id);
+
+ private:
+ // Removes "shape_id" from shape_ids_ if it exists, otherwise insert it.
+ void ToggleShape(int shape_id);
+
+ // Returns a pointer to the first entry "x" where x >= shape_id.
+ ShapeIdSet::iterator lower_bound(int32 shape_id);
+
+ bool is_active_;
+ S2Point a_, b_;
+ S2CellId next_cellid_;
+ S2EdgeCrosser crosser_;
+ ShapeIdSet shape_ids_;
+
+ // Shape ids saved by SaveAndClearStateBefore(). The state is never saved
+ // recursively so we don't need to worry about maintaining a stack.
+ ShapeIdSet saved_ids_;
+};
+
+// As shapes are added, we compute which ones contain the start of the
+// S2CellId space-filling curve by drawing an edge from S2::Origin() to this
+// point and counting how many shape edges cross this edge.
+MutableS2ShapeIndex::InteriorTracker::InteriorTracker()
+ : is_active_(false), b_(Origin()),
+ next_cellid_(S2CellId::Begin(S2CellId::kMaxLevel)) {
+}
+
+S2Point MutableS2ShapeIndex::InteriorTracker::Origin() {
+ // The start of the S2CellId space-filling curve.
+ return S2::FaceUVtoXYZ(0, -1, -1).Normalize();
+}
+
+void MutableS2ShapeIndex::InteriorTracker::AddShape(int32 shape_id,
+ bool contains_focus) {
+ is_active_ = true;
+ if (contains_focus) {
+ ToggleShape(shape_id);
+ }
+}
+
+void MutableS2ShapeIndex::InteriorTracker::ToggleShape(int shape_id) {
+ // Since shape_ids_.size() is typically *very* small (0, 1, or 2), it turns
+ // out to be significantly faster to maintain a sorted array rather than
+ // using an STL set or btree_set.
+ if (shape_ids_.empty()) {
+ shape_ids_.push_back(shape_id);
+ } else if (shape_ids_[0] == shape_id) {
+ shape_ids_.erase(shape_ids_.begin());
+ } else {
+ ShapeIdSet::iterator pos = shape_ids_.begin();
+ while (*pos < shape_id) {
+ if (++pos == shape_ids_.end()) {
+ shape_ids_.push_back(shape_id);
+ return;
+ }
+ }
+ if (*pos == shape_id) {
+ shape_ids_.erase(pos);
+ } else {
+ shape_ids_.insert(pos, shape_id);
+ }
+ }
+}
+
+void MutableS2ShapeIndex::InteriorTracker::DrawTo(const S2Point& b) {
+ a_ = b_;
+ b_ = b;
+ crosser_.Init(&a_, &b_);
+}
+
+inline void MutableS2ShapeIndex::InteriorTracker::TestEdge(
+ int32 shape_id, const S2Shape::Edge& edge) {
+ if (crosser_.EdgeOrVertexCrossing(&edge.v0, &edge.v1)) {
+ ToggleShape(shape_id);
+ }
+}
+
+// Like std::lower_bound(shape_ids_.begin(), shape_ids_.end(), shape_id), but
+// implemented with linear rather than binary search because the number of
+// shapes being tracked is typically very small.
+inline MutableS2ShapeIndex::ShapeIdSet::iterator
+MutableS2ShapeIndex::InteriorTracker::lower_bound(int32 shape_id) {
+ ShapeIdSet::iterator pos = shape_ids_.begin();
+ while (pos != shape_ids_.end() && *pos < shape_id) { ++pos; }
+ return pos;
+}
+
+void MutableS2ShapeIndex::InteriorTracker::SaveAndClearStateBefore(
+ int32 limit_shape_id) {
+ S2_DCHECK(saved_ids_.empty());
+ ShapeIdSet::iterator limit = lower_bound(limit_shape_id);
+ saved_ids_.assign(shape_ids_.begin(), limit);
+ shape_ids_.erase(shape_ids_.begin(), limit);
+}
+
+void MutableS2ShapeIndex::InteriorTracker::RestoreStateBefore(
+ int32 limit_shape_id) {
+ shape_ids_.erase(shape_ids_.begin(), lower_bound(limit_shape_id));
+ shape_ids_.insert(shape_ids_.begin(), saved_ids_.begin(), saved_ids_.end());
+ saved_ids_.clear();
+}
+
+// Apply any pending updates in a thread-safe way.
+void MutableS2ShapeIndex::ApplyUpdatesThreadSafe() {
+ lock_.Lock();
+ if (index_status_.load(std::memory_order_relaxed) == FRESH) {
+ lock_.Unlock();
+ } else if (index_status_.load(std::memory_order_relaxed) == UPDATING) {
+ // Wait until the updating thread is finished. We do this by attempting
+ // to lock a mutex that is held by the updating thread. When this mutex
+ // is unlocked the index_status_ is guaranteed to be FRESH.
+ ++update_state_->num_waiting;
+ lock_.Unlock();
+ update_state_->wait_mutex.Lock();
+ lock_.Lock();
+ --update_state_->num_waiting;
+ UnlockAndSignal(); // Notify other waiting threads.
+ } else {
+ S2_DCHECK_EQ(STALE, index_status_);
+ index_status_.store(UPDATING, std::memory_order_relaxed);
+ // Allocate the extra state needed for thread synchronization. We keep
+ // the spinlock held while doing this, because (1) memory allocation is
+ // fast, so the chance of a context switch while holding the lock is low;
+ // (2) by far the most common situation is that there is no contention,
+ // and this saves an extra lock and unlock step; (3) even in the rare case
+ // where there is contention, the main side effect is that some other
+ // thread will burn a few CPU cycles rather than sleeping.
+ update_state_.reset(new UpdateState);
+ // lock_.Lock wait_mutex *before* calling Unlock() to ensure that all other
+ // threads will block on it.
+ update_state_->wait_mutex.Lock();
+ // Release the spinlock before doing any real work.
+ lock_.Unlock();
+ ApplyUpdatesInternal();
+ lock_.Lock();
+ // index_status_ can be updated to FRESH only while locked *and* using
+ // an atomic store operation, so that MaybeApplyUpdates() can check
+ // whether the index is FRESH without acquiring the spinlock.
+ index_status_.store(FRESH, std::memory_order_release);
+ UnlockAndSignal(); // Notify any waiting threads.
+ }
+}
+
+// Releases lock_ and wakes up any waiting threads by releasing wait_mutex.
+// If this was the last waiting thread, also deletes update_state_.
+// REQUIRES: lock_ is held.
+// REQUIRES: wait_mutex is held.
+inline void MutableS2ShapeIndex::UnlockAndSignal() {
+ S2_DCHECK_EQ(FRESH, index_status_);
+ int num_waiting = update_state_->num_waiting;
+ lock_.Unlock();
+ // Allow another waiting thread to proceed. Note that no new threads can
+ // start waiting because the index_status_ is now FRESH, and the caller is
+ // required to prevent any new mutations from occurring while these const
+ // methods are running.
+ //
+ // We need to unlock wait_mutex before destroying it even if there are no
+ // waiting threads.
+ update_state_->wait_mutex.Unlock();
+ if (num_waiting == 0) {
+ update_state_.reset();
+ }
+}
+
+void MutableS2ShapeIndex::ForceBuild() {
+ // No locks required because this is not a const method. It is the client's
+ // responsibility to ensure correct thread synchronization.
+ if (index_status_.load(std::memory_order_relaxed) != FRESH) {
+ ApplyUpdatesInternal();
+ index_status_.store(FRESH, std::memory_order_relaxed);
+ }
+}
+
+// A BatchDescriptor represents a set of pending updates that will be applied
+// at the same time. The batch consists of all updates with shape ids between
+// the current value of "ShapeIndex::pending_additions_begin_" (inclusive) and
+// "additions_end" (exclusive). The first batch to be processed also
+// implicitly includes all shapes being removed. "num_edges" is the total
+// number of edges that will be added or removed in this batch.
+struct MutableS2ShapeIndex::BatchDescriptor {
+ BatchDescriptor(int _additions_end, int _num_edges)
+ : additions_end(_additions_end), num_edges(_num_edges) {
+ }
+ int additions_end;
+ int num_edges;
+};
+
+// This method updates the index by applying all pending additions and
+// removals. It does *not* update index_status_ (see ApplyUpdatesThreadSafe).
+void MutableS2ShapeIndex::ApplyUpdatesInternal() {
+ // Check whether we have so many edges to process that we should process
+ // them in multiple batches to save memory. Building the index can use up
+ // to 20x as much memory (per edge) as the final index size.
+ vector<BatchDescriptor> batches;
+ GetUpdateBatches(&batches);
+ int i = 0;
+ for (const BatchDescriptor& batch : batches) {
+ vector<FaceEdge> all_edges[6];
+ S2_VLOG(1) << "Batch " << i++ << ": shape_limit=" << batch.additions_end
+ << ", edges=" << batch.num_edges;
+
+ ReserveSpace(batch, all_edges);
+ InteriorTracker tracker;
+ if (pending_removals_) {
+ // The first batch implicitly includes all shapes being removed.
+ for (const auto& pending_removal : *pending_removals_) {
+ RemoveShape(pending_removal, all_edges, &tracker);
+ }
+ pending_removals_.reset(nullptr);
+ }
+ for (int id = pending_additions_begin_; id < batch.additions_end; ++id) {
+ AddShape(id, all_edges, &tracker);
+ }
+ for (int face = 0; face < 6; ++face) {
+ UpdateFaceEdges(face, all_edges[face], &tracker);
+ // Save memory by clearing vectors after we are done with them.
+ vector<FaceEdge>().swap(all_edges[face]);
+ }
+ pending_additions_begin_ = batch.additions_end;
+ }
+ // It is the caller's responsibility to update index_status_.
+}
+
+// Count the number of edges being updated, and break them into several
+// batches if necessary to reduce the amount of memory needed. (See the
+// documentation for FLAGS_s2shape_index_tmp_memory_budget_mb.)
+void MutableS2ShapeIndex::GetUpdateBatches(vector<BatchDescriptor>* batches)
+ const {
+ // Count the edges being removed and added.
+ int num_edges_removed = 0;
+ if (pending_removals_) {
+ for (const auto& pending_removal : *pending_removals_) {
+ num_edges_removed += pending_removal.edges.size();
+ }
+ }
+ int num_edges_added = 0;
+ for (int id = pending_additions_begin_; id < shapes_.size(); ++id) {
+ const S2Shape* shape = this->shape(id);
+ if (shape == nullptr) continue;
+ num_edges_added += shape->num_edges();
+ }
+ int num_edges = num_edges_removed + num_edges_added;
+
+ // The following memory estimates are based on heap profiling.
+ //
+ // The final size of a MutableS2ShapeIndex depends mainly on how finely the
+ // index is subdivided, as controlled by Options::max_edges_per_cell() and
+ // --s2shape_index_default_max_edges_per_cell. For realistic values of
+ // max_edges_per_cell() and shapes with moderate numbers of edges, it is
+ // difficult to get much below 8 bytes per edge. [The minimum possible size
+ // is 4 bytes per edge (to store a 32-bit edge id in an S2ClippedShape) plus
+ // 24 bytes per shape (for the S2ClippedShape itself plus a pointer in the
+ // shapes_ vector.]
+ //
+ // The temporary memory consists mainly of the FaceEdge and ClippedEdge
+ // structures plus a ClippedEdge pointer for every level of recursive
+ // subdivision. For very large indexes this can be 200 bytes per edge.
+ const size_t kFinalBytesPerEdge = 8;
+ const size_t kTmpBytesPerEdge = 200;
+ const size_t kTmpMemoryBudgetBytes =
+ static_cast<size_t>(FLAGS_s2shape_index_tmp_memory_budget_mb) << 20;
+
+ // We arbitrarily limit the number of batches just as a safety measure.
+ // With the current default memory budget of 100 MB, this limit is not
+ // reached even when building an index of 350 million edges.
+ const int kMaxUpdateBatches = 100;
+
+ if (num_edges * kTmpBytesPerEdge <= kTmpMemoryBudgetBytes) {
+ // We can update all edges at once without exceeding kTmpMemoryBudgetBytes.
+ batches->push_back(BatchDescriptor(shapes_.size(), num_edges));
+ return;
+ }
+ // Otherwise, break the updates into up to several batches, where the size
+ // of each batch is chosen so that all batches use approximately the same
+ // high-water memory. GetBatchSizes() returns the recommended number of
+ // edges in each batch.
+ vector<int> batch_sizes;
+ GetBatchSizes(num_edges, kMaxUpdateBatches, kFinalBytesPerEdge,
+ kTmpBytesPerEdge, kTmpMemoryBudgetBytes, &batch_sizes);
+
+ // We always process removed edges in a single batch, since (1) they already
+ // take up a lot of memory because we have copied all their edges, and (2)
+ // AbsorbIndexCell() uses (shapes_[id] == nullptr) to detect when a shape is
+ // being removed, so in order to split the removals into batches we would
+ // need a different approach (e.g., temporarily add fake entries to shapes_
+ // and restore them back to nullptr as shapes are actually removed).
+ num_edges = 0;
+ if (pending_removals_) {
+ num_edges += num_edges_removed;
+ if (num_edges >= batch_sizes[0]) {
+ batches->push_back(BatchDescriptor(pending_additions_begin_, num_edges));
+ num_edges = 0;
+ }
+ }
+ // Keep adding shapes to each batch until the recommended number of edges
+ // for that batch is reached, then move on to the next batch.
+ for (int id = pending_additions_begin_; id < shapes_.size(); ++id) {
+ const S2Shape* shape = this->shape(id);
+ if (shape == nullptr) continue;
+ num_edges += shape->num_edges();
+ if (num_edges >= batch_sizes[batches->size()]) {
+ batches->push_back(BatchDescriptor(id + 1, num_edges));
+ num_edges = 0;
+ }
+ }
+ // Some shapes have no edges. If a shape with no edges is the last shape to
+ // be added or removed, then the final batch may not include it, so we fix
+ // that problem here.
+ batches->back().additions_end = shapes_.size();
+ S2_DCHECK_LE(batches->size(), kMaxUpdateBatches);
+}
+
+// Given "num_items" items, each of which uses "tmp_bytes_per_item" while it
+// is being updated but only "final_bytes_per_item" in the end, divide the
+// items into batches that have approximately the same *total* memory usage
+// consisting of the temporary memory needed for the items in the current
+// batch plus the final size of all the items that have already been
+// processed. Use the fewest number of batches (but never more than
+// "max_batches") such that the total memory usage does not exceed the
+// combined final size of all the items plus "tmp_memory_budget_bytes".
+/* static */
+void MutableS2ShapeIndex::GetBatchSizes(int num_items, int max_batches,
+ double final_bytes_per_item,
+ double tmp_bytes_per_item,
+ double tmp_memory_budget_bytes,
+ vector<int>* batch_sizes) {
+ // This code tries to fit all the data into the same memory space
+ // ("total_budget_bytes") at every iteration. The data consists of some
+ // number of processed items (at "final_bytes_per_item" each), plus some
+ // number being updated (at "tmp_bytes_per_item" each). The space occupied
+ // by the items being updated is the "free space". At each iteration, the
+ // free space is multiplied by (1 - final_bytes_per_item/tmp_bytes_per_item)
+ // as the items are converted into their final form.
+ double final_bytes = num_items * final_bytes_per_item;
+ double final_bytes_ratio = final_bytes_per_item / tmp_bytes_per_item;
+ double free_space_multiplier = 1 - final_bytes_ratio;
+
+ // The total memory budget is the greater of the final size plus the allowed
+ // temporary memory, or the minimum amount of memory required to limit the
+ // number of batches to "max_batches".
+ double total_budget_bytes = max(
+ final_bytes + tmp_memory_budget_bytes,
+ final_bytes / (1 - pow(free_space_multiplier, max_batches)));
+
+ // "max_batch_items" is the number of items in the current batch.
+ double max_batch_items = total_budget_bytes / tmp_bytes_per_item;
+ batch_sizes->clear();
+ for (int i = 0; i + 1 < max_batches && num_items > 0; ++i) {
+ int batch_items =
+ std::min(num_items, static_cast<int>(max_batch_items + 1));
+ batch_sizes->push_back(batch_items);
+ num_items -= batch_items;
+ max_batch_items *= free_space_multiplier;
+ }
+ S2_DCHECK_LE(batch_sizes->size(), max_batches);
+}
+
+// Reserve an appropriate amount of space for the top-level face edges in the
+// current batch. This data structure uses about half of the temporary memory
+// needed during index construction. Furthermore, if the arrays are grown via
+// push_back() then up to 10% of the total run time consists of copying data
+// as these arrays grow, so it is worthwhile to preallocate space for them.
+void MutableS2ShapeIndex::ReserveSpace(const BatchDescriptor& batch,
+ vector<FaceEdge> all_edges[6]) const {
+ // If the number of edges is relatively small, then the fastest approach is
+ // to simply reserve space on every face for the maximum possible number of
+ // edges. We use a different threshold for this calculation than for
+ // deciding when to break updates into batches, because the cost/benefit
+ // ratio is different. (Here the only extra expense is that we need to
+ // sample the edges to estimate how many edges per face there are.)
+ const size_t kMaxCheapBytes = 30 << 20; // 30 MB
+ const int kMaxCheapEdges = kMaxCheapBytes / (6 * sizeof(FaceEdge));
+ if (batch.num_edges <= kMaxCheapEdges) {
+ for (int face = 0; face < 6; ++face) {
+ all_edges[face].reserve(batch.num_edges);
+ }
+ return;
+ }
+ // Otherwise we estimate the number of edges on each face by taking a random
+ // sample. The goal is to come up with an estimate that is fast and
+ // accurate for non-pathological geometry. If our estimates happen to be
+ // wrong, the vector will still grow automatically - the main side effects
+ // are that memory usage will be larger (by up to a factor of 3), and
+ // constructing the index will be about 10% slower.
+ //
+ // Given a desired sample size, we choose equally spaced edges from
+ // throughout the entire data set. We use a Bresenham-type algorithm to
+ // choose the samples.
+ const int kDesiredSampleSize = 10000;
+ const int sample_interval = max(1, batch.num_edges / kDesiredSampleSize);
+
+ // Initialize "edge_id" to be midway through the first sample interval.
+ // Because samples are equally spaced the actual sample size may differ
+ // slightly from the desired sample size.
+ int edge_id = sample_interval / 2;
+ const int actual_sample_size = (batch.num_edges + edge_id) / sample_interval;
+ int face_count[6] = { 0, 0, 0, 0, 0, 0 };
+ if (pending_removals_) {
+ for (const RemovedShape& removed : *pending_removals_) {
+ edge_id += removed.edges.size();
+ while (edge_id >= sample_interval) {
+ edge_id -= sample_interval;
+ face_count[S2::GetFace(removed.edges[edge_id].v0)] += 1;
+ }
+ }
+ }
+ for (int id = pending_additions_begin_; id < batch.additions_end; ++id) {
+ const S2Shape* shape = this->shape(id);
+ if (shape == nullptr) continue;
+ edge_id += shape->num_edges();
+ while (edge_id >= sample_interval) {
+ edge_id -= sample_interval;
+ // For speed, we only count the face containing one endpoint of the
+ // edge. In general the edge could span all 6 faces (with padding), but
+ // it's not worth the expense to compute this more accurately.
+ face_count[S2::GetFace(shape->edge(edge_id).v0)] += 1;
+ }
+ }
+ // Now given the raw face counts, compute a confidence interval such that we
+ // will be unlikely to allocate too little space. Computing accurate
+ // binomial confidence intervals is expensive and not really necessary.
+ // Instead we use a simple approximation:
+ // - For any face with at least 1 sample, we use at least a 4-sigma
+ // confidence interval. (The chosen width is adequate for the worst case
+ // accuracy, which occurs when the face contains approximately 50% of the
+ // edges.) Assuming that our sample is representative, the probability of
+ // reserving too little space is approximately 1 in 30,000.
+ // - For faces with no samples at all, we don't bother reserving space.
+ // It is quite likely that such faces are truly empty, so we save time
+ // and memory this way. If the face does contain some edges, there will
+ // only be a few so it is fine to let the vector grow automatically.
+ // On average, we reserve 2% extra space for each face that has geometry.
+
+ // kMaxSemiWidth is the maximum semi-width over all probabilities p of a
+ // 4-sigma binomial confidence interval with a sample size of 10,000.
+ const double kMaxSemiWidth = 0.02;
+ const double sample_ratio = 1.0 / actual_sample_size;
+ for (int face = 0; face < 6; ++face) {
+ if (face_count[face] == 0) continue;
+ double fraction = sample_ratio * face_count[face] + kMaxSemiWidth;
+ all_edges[face].reserve(fraction * batch.num_edges);
+ }
+}
+
+// Clip all edges of the given shape to the six cube faces, add the clipped
+// edges to "all_edges", and start tracking its interior if necessary.
+void MutableS2ShapeIndex::AddShape(int id, vector<FaceEdge> all_edges[6],
+ InteriorTracker* tracker) const {
+ const S2Shape* shape = this->shape(id);
+ if (shape == nullptr) {
+ return; // This shape has already been removed.
+ }
+ // Construct a template for the edges to be added.
+ FaceEdge edge;
+ edge.shape_id = id;
+ edge.has_interior = (shape->dimension() == 2);
+ if (edge.has_interior) {
+ tracker->AddShape(id, s2shapeutil::ContainsBruteForce(*shape,
+ tracker->focus()));
+ }
+ int num_edges = shape->num_edges();
+ for (int e = 0; e < num_edges; ++e) {
+ edge.edge_id = e;
+ edge.edge = shape->edge(e);
+ edge.max_level = GetEdgeMaxLevel(edge.edge);
+ AddFaceEdge(&edge, all_edges);
+ }
+}
+
+void MutableS2ShapeIndex::RemoveShape(const RemovedShape& removed,
+ vector<FaceEdge> all_edges[6],
+ InteriorTracker* tracker) const {
+ FaceEdge edge;
+ edge.edge_id = -1; // Not used or needed for removed edges.
+ edge.shape_id = removed.shape_id;
+ edge.has_interior = removed.has_interior;
+ if (edge.has_interior) {
+ tracker->AddShape(edge.shape_id, removed.contains_tracker_origin);
+ }
+ for (const auto& removed_edge : removed.edges) {
+ edge.edge = removed_edge;
+ edge.max_level = GetEdgeMaxLevel(edge.edge);
+ AddFaceEdge(&edge, all_edges);
+ }
+}
+
+inline void MutableS2ShapeIndex::AddFaceEdge(
+ FaceEdge* edge, vector<FaceEdge> all_edges[6]) const {
+ // Fast path: both endpoints are on the same face, and are far enough from
+ // the edge of the face that don't intersect any (padded) adjacent face.
+ int a_face = S2::GetFace(edge->edge.v0);
+ if (a_face == S2::GetFace(edge->edge.v1)) {
+ S2::ValidFaceXYZtoUV(a_face, edge->edge.v0, &edge->a);
+ S2::ValidFaceXYZtoUV(a_face, edge->edge.v1, &edge->b);
+ const double kMaxUV = 1 - kCellPadding;
+ if (fabs(edge->a[0]) <= kMaxUV && fabs(edge->a[1]) <= kMaxUV &&
+ fabs(edge->b[0]) <= kMaxUV && fabs(edge->b[1]) <= kMaxUV) {
+ all_edges[a_face].push_back(*edge);
+ return;
+ }
+ }
+ // Otherwise we simply clip the edge to all six faces.
+ for (int face = 0; face < 6; ++face) {
+ if (S2::ClipToPaddedFace(edge->edge.v0, edge->edge.v1, face,
+ kCellPadding, &edge->a, &edge->b)) {
+ all_edges[face].push_back(*edge);
+ }
+ }
+}
+
+// Return the first level at which the edge will *not* contribute towards
+// the decision to subdivide.
+int MutableS2ShapeIndex::GetEdgeMaxLevel(const S2Shape::Edge& edge) const {
+ // Compute the maximum cell size for which this edge is considered "long".
+ // The calculation does not need to be perfectly accurate, so we use Norm()
+ // rather than Angle() for speed.
+ double cell_size = ((edge.v0 - edge.v1).Norm() *
+ FLAGS_s2shape_index_cell_size_to_long_edge_ratio);
+ // Now return the first level encountered during subdivision where the
+ // average cell size is at most "cell_size".
+ return S2::kAvgEdge.GetLevelForMaxValue(cell_size);
+}
+
+// EdgeAllocator provides temporary storage for new ClippedEdges that are
+// created during indexing. It is essentially a stack model, where edges are
+// allocated as the recursion does down and freed as it comes back up.
+//
+// It also provides a mutable vector of FaceEdges that is used when
+// incrementally updating the index (see AbsorbIndexCell).
+class MutableS2ShapeIndex::EdgeAllocator {
+ public:
+ EdgeAllocator() : size_(0) {}
+
+ // Return a pointer to a newly allocated edge. The EdgeAllocator
+ // retains ownership.
+ ClippedEdge* NewClippedEdge() {
+ if (size_ == clipped_edges_.size()) {
+ clipped_edges_.emplace_back(new ClippedEdge);
+ }
+ return clipped_edges_[size_++].get();
+ }
+ // Return the number of allocated edges.
+ size_t size() const { return size_; }
+
+ // Reset the allocator to only contain the first "size" allocated edges.
+ void Reset(size_t size) { size_ = size; }
+
+ vector<FaceEdge>* mutable_face_edges() {
+ return &face_edges_;
+ }
+
+ private:
+ // We can't use vector<ClippedEdge> because edges are not allowed to move
+ // once they have been allocated. Instead we keep a pool of allocated edges
+ // that are all deleted together at the end.
+ size_t size_;
+ vector<unique_ptr<ClippedEdge>> clipped_edges_;
+
+ // On the other hand, we can use vector<FaceEdge> because they are allocated
+ // only at one level during the recursion (namely, the level at which we
+ // absorb an existing index cell).
+ vector<FaceEdge> face_edges_;
+
+ EdgeAllocator(const EdgeAllocator&) = delete;
+ void operator=(const EdgeAllocator&) = delete;
+};
+
+// Given a face and a vector of edges that intersect that face, add or remove
+// all the edges from the index. (An edge is added if shapes_[id] is not
+// nullptr, and removed otherwise.)
+void MutableS2ShapeIndex::UpdateFaceEdges(int face,
+ const vector<FaceEdge>& face_edges,
+ InteriorTracker* tracker) {
+ int num_edges = face_edges.size();
+ if (num_edges == 0 && tracker->shape_ids().empty()) return;
+
+ // Create the initial ClippedEdge for each FaceEdge. Additional clipped
+ // edges are created when edges are split between child cells. We create
+ // two arrays, one containing the edge data and another containing pointers
+ // to those edges, so that during the recursion we only need to copy
+ // pointers in order to propagate an edge to the correct child.
+ vector<ClippedEdge> clipped_edge_storage;
+ vector<const ClippedEdge*> clipped_edges;
+ clipped_edge_storage.reserve(num_edges);
+ clipped_edges.reserve(num_edges);
+ R2Rect bound = R2Rect::Empty();
+ for (int e = 0; e < num_edges; ++e) {
+ ClippedEdge clipped;
+ clipped.face_edge = &face_edges[e];
+ clipped.bound = R2Rect::FromPointPair(face_edges[e].a, face_edges[e].b);
+ clipped_edge_storage.push_back(clipped);
+ clipped_edges.push_back(&clipped_edge_storage.back());
+ bound.AddRect(clipped.bound);
+ }
+ // Construct the initial face cell containing all the edges, and then update
+ // all the edges in the index recursively.
+ EdgeAllocator alloc;
+ S2CellId face_id = S2CellId::FromFace(face);
+ S2PaddedCell pcell(face_id, kCellPadding);
+
+ // "disjoint_from_index" means that the current cell being processed (and
+ // all its descendants) are not already present in the index.
+ bool disjoint_from_index = is_first_update();
+ if (num_edges > 0) {
+ S2CellId shrunk_id = ShrinkToFit(pcell, bound);
+ if (shrunk_id != pcell.id()) {
+ // All the edges are contained by some descendant of the face cell. We
+ // can save a lot of work by starting directly with that cell, but if we
+ // are in the interior of at least one shape then we need to create
+ // index entries for the cells we are skipping over.
+ SkipCellRange(face_id.range_min(), shrunk_id.range_min(),
+ tracker, &alloc, disjoint_from_index);
+ pcell = S2PaddedCell(shrunk_id, kCellPadding);
+ UpdateEdges(pcell, &clipped_edges, tracker, &alloc, disjoint_from_index);
+ SkipCellRange(shrunk_id.range_max().next(), face_id.range_max().next(),
+ tracker, &alloc, disjoint_from_index);
+ return;
+ }
+ }
+ // Otherwise (no edges, or no shrinking is possible), subdivide normally.
+ UpdateEdges(pcell, &clipped_edges, tracker, &alloc, disjoint_from_index);
+}
+
+inline S2CellId MutableS2ShapeIndex::ShrinkToFit(const S2PaddedCell& pcell,
+ const R2Rect& bound) const {
+ S2CellId shrunk_id = pcell.ShrinkToFit(bound);
+ if (!is_first_update() && shrunk_id != pcell.id()) {
+ // Don't shrink any smaller than the existing index cells, since we need
+ // to combine the new edges with those cells.
+ // Use InitStale() to avoid applying updated recursively.
+ Iterator iter;
+ iter.InitStale(this);
+ CellRelation r = iter.Locate(shrunk_id);
+ if (r == INDEXED) { shrunk_id = iter.id(); }
+ }
+ return shrunk_id;
+}
+
+// Skip over the cells in the given range, creating index cells if we are
+// currently in the interior of at least one shape.
+void MutableS2ShapeIndex::SkipCellRange(S2CellId begin, S2CellId end,
+ InteriorTracker* tracker,
+ EdgeAllocator* alloc,
+ bool disjoint_from_index) {
+ // If we aren't in the interior of a shape, then skipping over cells is easy.
+ if (tracker->shape_ids().empty()) return;
+
+ // Otherwise generate the list of cell ids that we need to visit, and create
+ // an index entry for each one.
+ for (S2CellId skipped_id : S2CellUnion::FromBeginEnd(begin, end)) {
+ vector<const ClippedEdge*> clipped_edges;
+ UpdateEdges(S2PaddedCell(skipped_id, kCellPadding),
+ &clipped_edges, tracker, alloc, disjoint_from_index);
+ }
+}
+
+// Given a cell and a set of ClippedEdges whose bounding boxes intersect that
+// cell, add or remove all the edges from the index. Temporary space for
+// edges that need to be subdivided is allocated from the given EdgeAllocator.
+// "disjoint_from_index" is an optimization hint indicating that cell_map_
+// does not contain any entries that overlap the given cell.
+void MutableS2ShapeIndex::UpdateEdges(const S2PaddedCell& pcell,
+ vector<const ClippedEdge*>* edges,
+ InteriorTracker* tracker,
+ EdgeAllocator* alloc,
+ bool disjoint_from_index) {
+ // Cases where an index cell is not needed should be detected before this.
+ S2_DCHECK(!edges->empty() || !tracker->shape_ids().empty());
+
+ // This function is recursive with a maximum recursion depth of 30
+ // (S2CellId::kMaxLevel). Note that using an explicit stack does not seem
+ // to be any faster based on profiling.
+
+ // Incremental updates are handled as follows. All edges being added or
+ // removed are combined together in "edges", and all shapes with interiors
+ // are tracked using "tracker". We subdivide recursively as usual until we
+ // encounter an existing index cell. At this point we "absorb" the index
+ // cell as follows:
+ //
+ // - Edges and shapes that are being removed are deleted from "edges" and
+ // "tracker".
+ // - All remaining edges and shapes from the index cell are added to
+ // "edges" and "tracker".
+ // - Continue subdividing recursively, creating new index cells as needed.
+ // - When the recursion gets back to the cell that was absorbed, we
+ // restore "edges" and "tracker" to their previous state.
+ //
+ // Note that the only reason that we include removed shapes in the recursive
+ // subdivision process is so that we can find all of the index cells that
+ // contain those shapes efficiently, without maintaining an explicit list of
+ // index cells for each shape (which would be expensive in terms of memory).
+ bool index_cell_absorbed = false;
+ if (!disjoint_from_index) {
+ // There may be existing index cells contained inside "pcell". If we
+ // encounter such a cell, we need to combine the edges being updated with
+ // the existing cell contents by "absorbing" the cell.
+ // Use InitStale() to avoid applying updated recursively.
+ Iterator iter;
+ iter.InitStale(this);
+ CellRelation r = iter.Locate(pcell.id());
+ if (r == DISJOINT) {
+ disjoint_from_index = true;
+ } else if (r == INDEXED) {
+ // Absorb the index cell by transferring its contents to "edges" and
+ // deleting it. We also start tracking the interior of any new shapes.
+ AbsorbIndexCell(pcell, iter, edges, tracker, alloc);
+ index_cell_absorbed = true;
+ disjoint_from_index = true;
+ } else {
+ S2_DCHECK_EQ(SUBDIVIDED, r);
+ }
+ }
+
+ // If there are existing index cells below us, then we need to keep
+ // subdividing so that we can merge with those cells. Otherwise,
+ // MakeIndexCell checks if the number of edges is small enough, and creates
+ // an index cell if possible (returning true when it does so).
+ if (!disjoint_from_index || !MakeIndexCell(pcell, *edges, tracker)) {
+ // Reserve space for the edges that will be passed to each child. This is
+ // important since otherwise the running time is dominated by the time
+ // required to grow the vectors. The amount of memory involved is
+ // relatively small, so we simply reserve the maximum space for every child.
+ vector<const ClippedEdge*> child_edges[2][2]; // [i][j]
+ int num_edges = edges->size();
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 2; ++j) {
+ child_edges[i][j].reserve(num_edges);
+ }
+ }
+
+ // Remember the current size of the EdgeAllocator so that we can free any
+ // edges that are allocated during edge splitting.
+ size_t alloc_size = alloc->size();
+
+ // Compute the middle of the padded cell, defined as the rectangle in
+ // (u,v)-space that belongs to all four (padded) children. By comparing
+ // against the four boundaries of "middle" we can determine which children
+ // each edge needs to be propagated to.
+ const R2Rect& middle = pcell.middle();
+
+ // Build up a vector edges to be passed to each child cell. The (i,j)
+ // directions are left (i=0), right (i=1), lower (j=0), and upper (j=1).
+ // Note that the vast majority of edges are propagated to a single child.
+ // This case is very fast, consisting of between 2 and 4 floating-point
+ // comparisons and copying one pointer. (ClipVAxis is inline.)
+ for (int e = 0; e < num_edges; ++e) {
+ const ClippedEdge* edge = (*edges)[e];
+ if (edge->bound[0].hi() <= middle[0].lo()) {
+ // Edge is entirely contained in the two left children.
+ ClipVAxis(edge, middle[1], child_edges[0], alloc);
+ } else if (edge->bound[0].lo() >= middle[0].hi()) {
+ // Edge is entirely contained in the two right children.
+ ClipVAxis(edge, middle[1], child_edges[1], alloc);
+ } else if (edge->bound[1].hi() <= middle[1].lo()) {
+ // Edge is entirely contained in the two lower children.
+ child_edges[0][0].push_back(ClipUBound(edge, 1, middle[0].hi(), alloc));
+ child_edges[1][0].push_back(ClipUBound(edge, 0, middle[0].lo(), alloc));
+ } else if (edge->bound[1].lo() >= middle[1].hi()) {
+ // Edge is entirely contained in the two upper children.
+ child_edges[0][1].push_back(ClipUBound(edge, 1, middle[0].hi(), alloc));
+ child_edges[1][1].push_back(ClipUBound(edge, 0, middle[0].lo(), alloc));
+ } else {
+ // The edge bound spans all four children. The edge itself intersects
+ // either three or four (padded) children.
+ const ClippedEdge* left = ClipUBound(edge, 1, middle[0].hi(), alloc);
+ ClipVAxis(left, middle[1], child_edges[0], alloc);
+ const ClippedEdge* right = ClipUBound(edge, 0, middle[0].lo(), alloc);
+ ClipVAxis(right, middle[1], child_edges[1], alloc);
+ }
+ }
+ // Free any memory reserved for children that turned out to be empty. This
+ // step is cheap and reduces peak memory usage by about 10% when building
+ // large indexes (> 10M edges).
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 2; ++j) {
+ if (child_edges[i][j].empty()) {
+ vector<const ClippedEdge*>().swap(child_edges[i][j]);
+ }
+ }
+ }
+ // Now recursively update the edges in each child. We call the children in
+ // increasing order of S2CellId so that when the index is first constructed,
+ // all insertions into cell_map_ are at the end (which is much faster).
+ for (int pos = 0; pos < 4; ++pos) {
+ int i, j;
+ pcell.GetChildIJ(pos, &i, &j);
+ if (!child_edges[i][j].empty() || !tracker->shape_ids().empty()) {
+ UpdateEdges(S2PaddedCell(pcell, i, j), &child_edges[i][j],
+ tracker, alloc, disjoint_from_index);
+ }
+ }
+ // Free any temporary edges that were allocated during clipping.
+ alloc->Reset(alloc_size);
+ }
+ if (index_cell_absorbed) {
+ // Restore the state for any edges being removed that we are tracking.
+ tracker->RestoreStateBefore(pending_additions_begin_);
+ }
+}
+
+// Given an edge and an interval "middle" along the v-axis, clip the edge
+// against the boundaries of "middle" and add the edge to the corresponding
+// children.
+/* static */
+inline void MutableS2ShapeIndex::ClipVAxis(
+ const ClippedEdge* edge,
+ const R1Interval& middle,
+ vector<const ClippedEdge*> child_edges[2],
+ EdgeAllocator* alloc) {
+ if (edge->bound[1].hi() <= middle.lo()) {
+ // Edge is entirely contained in the lower child.
+ child_edges[0].push_back(edge);
+ } else if (edge->bound[1].lo() >= middle.hi()) {
+ // Edge is entirely contained in the upper child.
+ child_edges[1].push_back(edge);
+ } else {
+ // The edge bound spans both children.
+ child_edges[0].push_back(ClipVBound(edge, 1, middle.hi(), alloc));
+ child_edges[1].push_back(ClipVBound(edge, 0, middle.lo(), alloc));
+ }
+}
+
+// Given an edge, clip the given endpoint (lo=0, hi=1) of the u-axis so that
+// it does not extend past the given value.
+/* static */
+const MutableS2ShapeIndex::ClippedEdge*
+MutableS2ShapeIndex::ClipUBound(const ClippedEdge* edge, int u_end, double u,
+ EdgeAllocator* alloc) {
+ // First check whether the edge actually requires any clipping. (Sometimes
+ // this method is called when clipping is not necessary, e.g. when one edge
+ // endpoint is in the overlap area between two padded child cells.)
+ if (u_end == 0) {
+ if (edge->bound[0].lo() >= u) return edge;
+ } else {
+ if (edge->bound[0].hi() <= u) return edge;
+ }
+ // We interpolate the new v-value from the endpoints of the original edge.
+ // This has two advantages: (1) we don't need to store the clipped endpoints
+ // at all, just their bounding box; and (2) it avoids the accumulation of
+ // roundoff errors due to repeated interpolations. The result needs to be
+ // clamped to ensure that it is in the appropriate range.
+ const FaceEdge& e = *edge->face_edge;
+ double v = edge->bound[1].Project(
+ S2::InterpolateDouble(u, e.a[0], e.b[0], e.a[1], e.b[1]));
+
+ // Determine which endpoint of the v-axis bound to update. If the edge
+ // slope is positive we update the same endpoint, otherwise we update the
+ // opposite endpoint.
+ int v_end = u_end ^ ((e.a[0] > e.b[0]) != (e.a[1] > e.b[1]));
+ return UpdateBound(edge, u_end, u, v_end, v, alloc);
+}
+
+// Given an edge, clip the given endpoint (lo=0, hi=1) of the v-axis so that
+// it does not extend past the given value.
+/* static */
+const MutableS2ShapeIndex::ClippedEdge*
+MutableS2ShapeIndex::ClipVBound(const ClippedEdge* edge, int v_end, double v,
+ EdgeAllocator* alloc) {
+ // See comments in ClipUBound.
+ if (v_end == 0) {
+ if (edge->bound[1].lo() >= v) return edge;
+ } else {
+ if (edge->bound[1].hi() <= v) return edge;
+ }
+ const FaceEdge& e = *edge->face_edge;
+ double u = edge->bound[0].Project(
+ S2::InterpolateDouble(v, e.a[1], e.b[1], e.a[0], e.b[0]));
+ int u_end = v_end ^ ((e.a[0] > e.b[0]) != (e.a[1] > e.b[1]));
+ return UpdateBound(edge, u_end, u, v_end, v, alloc);
+}
+
+// Given an edge and two bound endpoints that need to be updated, allocate and
+// return a new edge with the updated bound.
+/* static */
+inline const MutableS2ShapeIndex::ClippedEdge*
+MutableS2ShapeIndex::UpdateBound(const ClippedEdge* edge, int u_end, double u,
+ int v_end, double v, EdgeAllocator* alloc) {
+ ClippedEdge* clipped = alloc->NewClippedEdge();
+ clipped->face_edge = edge->face_edge;
+ clipped->bound[0][u_end] = u;
+ clipped->bound[1][v_end] = v;
+ clipped->bound[0][1-u_end] = edge->bound[0][1-u_end];
+ clipped->bound[1][1-v_end] = edge->bound[1][1-v_end];
+ S2_DCHECK(!clipped->bound.is_empty());
+ S2_DCHECK(edge->bound.Contains(clipped->bound));
+ return clipped;
+}
+
+// Absorb an index cell by transferring its contents to "edges" and/or
+// "tracker", and then delete this cell from the index. If "edges" includes
+// any edges that are being removed, this method also updates their
+// InteriorTracker state to correspond to the exit vertex of this cell, and
+// saves the InteriorTracker state by calling SaveAndClearStateBefore(). It
+// is the caller's responsibility to restore this state by calling
+// RestoreStateBefore() when processing of this cell is finished.
+void MutableS2ShapeIndex::AbsorbIndexCell(const S2PaddedCell& pcell,
+ const Iterator& iter,
+ vector<const ClippedEdge*>* edges,
+ InteriorTracker* tracker,
+ EdgeAllocator* alloc) {
+ S2_DCHECK_EQ(pcell.id(), iter.id());
+
+ // When we absorb a cell, we erase all the edges that are being removed.
+ // However when we are finished with this cell, we want to restore the state
+ // of those edges (since that is how we find all the index cells that need
+ // to be updated). The edges themselves are restored automatically when
+ // UpdateEdges returns from its recursive call, but the InteriorTracker
+ // state needs to be restored explicitly.
+ //
+ // Here we first update the InteriorTracker state for removed edges to
+ // correspond to the exit vertex of this cell, and then save the
+ // InteriorTracker state. This state will be restored by UpdateEdges when
+ // it is finished processing the contents of this cell.
+ if (tracker->is_active() && !edges->empty() &&
+ is_shape_being_removed((*edges)[0]->face_edge->shape_id)) {
+ // We probably need to update the InteriorTracker. ("Probably" because
+ // it's possible that all shapes being removed do not have interiors.)
+ if (!tracker->at_cellid(pcell.id())) {
+ tracker->MoveTo(pcell.GetEntryVertex());
+ }
+ tracker->DrawTo(pcell.GetExitVertex());
+ tracker->set_next_cellid(pcell.id().next());
+ for (const ClippedEdge* edge : *edges) {
+ const FaceEdge* face_edge = edge->face_edge;
+ if (!is_shape_being_removed(face_edge->shape_id)) {
+ break; // All shapes being removed come first.
+ }
+ if (face_edge->has_interior) {
+ tracker->TestEdge(face_edge->shape_id, face_edge->edge);
+ }
+ }
+ }
+ // Save the state of the edges being removed, so that it can be restored
+ // when we are finished processing this cell and its children. We don't
+ // need to save the state of the edges being added because they aren't being
+ // removed from "edges" and will therefore be updated normally as we visit
+ // this cell and its children.
+ tracker->SaveAndClearStateBefore(pending_additions_begin_);
+
+ // Create a FaceEdge for each edge in this cell that isn't being removed.
+ vector<FaceEdge>* face_edges = alloc->mutable_face_edges();
+ face_edges->clear();
+ bool tracker_moved = false;
+ const S2ShapeIndexCell& cell = iter.cell();
+ for (int s = 0; s < cell.num_clipped(); ++s) {
+ const S2ClippedShape& clipped = cell.clipped(s);
+ int shape_id = clipped.shape_id();
+ const S2Shape* shape = this->shape(shape_id);
+ if (shape == nullptr) continue; // This shape is being removed.
+ int num_edges = clipped.num_edges();
+
+ // If this shape has an interior, start tracking whether we are inside the
+ // shape. UpdateEdges() wants to know whether the entry vertex of this
+ // cell is inside the shape, but we only know whether the center of the
+ // cell is inside the shape, so we need to test all the edges against the
+ // line segment from the cell center to the entry vertex.
+ FaceEdge edge;
+ edge.shape_id = shape->id();
+ edge.has_interior = (shape->dimension() == 2);
+ if (edge.has_interior) {
+ tracker->AddShape(shape_id, clipped.contains_center());
+ // There might not be any edges in this entire cell (i.e., it might be
+ // in the interior of all shapes), so we delay updating the tracker
+ // until we see the first edge.
+ if (!tracker_moved && num_edges > 0) {
+ tracker->MoveTo(pcell.GetCenter());
+ tracker->DrawTo(pcell.GetEntryVertex());
+ tracker->set_next_cellid(pcell.id());
+ tracker_moved = true;
+ }
+ }
+ for (int i = 0; i < num_edges; ++i) {
+ int e = clipped.edge(i);
+ edge.edge_id = e;
+ edge.edge = shape->edge(e);
+ edge.max_level = GetEdgeMaxLevel(edge.edge);
+ if (edge.has_interior) tracker->TestEdge(shape_id, edge.edge);
+ if (!S2::ClipToPaddedFace(edge.edge.v0, edge.edge.v1, pcell.id().face(),
+ kCellPadding, &edge.a, &edge.b)) {
+ S2_LOG(DFATAL) << "Invariant failure in MutableS2ShapeIndex";
+ }
+ face_edges->push_back(edge);
+ }
+ }
+ // Now create a ClippedEdge for each FaceEdge, and put them in "new_edges".
+ vector<const ClippedEdge*> new_edges;
+ for (const FaceEdge& face_edge : *face_edges) {
+ ClippedEdge* clipped = alloc->NewClippedEdge();
+ clipped->face_edge = &face_edge;
+ clipped->bound = S2::GetClippedEdgeBound(face_edge.a, face_edge.b,
+ pcell.bound());
+ new_edges.push_back(clipped);
+ }
+ // Discard any edges from "edges" that are being removed, and append the
+ // remainder to "new_edges". (This keeps the edges sorted by shape id.)
+ for (int i = 0; i < edges->size(); ++i) {
+ const ClippedEdge* clipped = (*edges)[i];
+ if (!is_shape_being_removed(clipped->face_edge->shape_id)) {
+ new_edges.insert(new_edges.end(), edges->begin() + i, edges->end());
+ break;
+ }
+ }
+ // Update the edge list and delete this cell from the index.
+ edges->swap(new_edges);
+ cell_map_.erase(pcell.id());
+ delete &cell;
+}
+
+// Attempt to build an index cell containing the given edges, and return true
+// if successful. (Otherwise the edges should be subdivided further.)
+bool MutableS2ShapeIndex::MakeIndexCell(const S2PaddedCell& pcell,
+ const vector<const ClippedEdge*>& edges,
+ InteriorTracker* tracker) {
+ if (edges.empty() && tracker->shape_ids().empty()) {
+ // No index cell is needed. (In most cases this situation is detected
+ // before we get to this point, but this can happen when all shapes in a
+ // cell are removed.)
+ return true;
+ }
+
+ // Count the number of edges that have not reached their maximum level yet.
+ // Return false if there are too many such edges.
+ int count = 0;
+ for (const ClippedEdge* edge : edges) {
+ count += (pcell.level() < edge->face_edge->max_level);
+ if (count > options_.max_edges_per_cell())
+ return false;
+ }
+
+ // Possible optimization: Continue subdividing as long as exactly one child
+ // of "pcell" intersects the given edges. This can be done by finding the
+ // bounding box of all the edges and calling ShrinkToFit():
+ //
+ // S2CellId cellid = pcell.ShrinkToFit(GetRectBound(edges));
+ //
+ // Currently this is not beneficial; it slows down construction by 4-25%
+ // (mainly computing the union of the bounding rectangles) and also slows
+ // down queries (since more recursive clipping is required to get down to
+ // the level of a spatial index cell). But it may be worth trying again
+ // once "contains_center" is computed and all algorithms are modified to
+ // take advantage of it.
+
+ // We update the InteriorTracker as follows. For every S2Cell in the index
+ // we construct two edges: one edge from entry vertex of the cell to its
+ // center, and one from the cell center to its exit vertex. Here "entry"
+ // and "exit" refer the S2CellId ordering, i.e. the order in which points
+ // are encountered along the S2 space-filling curve. The exit vertex then
+ // becomes the entry vertex for the next cell in the index, unless there are
+ // one or more empty intervening cells, in which case the InteriorTracker
+ // state is unchanged because the intervening cells have no edges.
+
+ // Shift the InteriorTracker focus point to the center of the current cell.
+ if (tracker->is_active() && !edges.empty()) {
+ if (!tracker->at_cellid(pcell.id())) {
+ tracker->MoveTo(pcell.GetEntryVertex());
+ }
+ tracker->DrawTo(pcell.GetCenter());
+ TestAllEdges(edges, tracker);
+ }
+ // Allocate and fill a new index cell. To get the total number of shapes we
+ // need to merge the shapes associated with the intersecting edges together
+ // with the shapes that happen to contain the cell center.
+ const ShapeIdSet& cshape_ids = tracker->shape_ids();
+ int num_shapes = CountShapes(edges, cshape_ids);
+ S2ShapeIndexCell* cell = new S2ShapeIndexCell;
+ S2ClippedShape* base = cell->add_shapes(num_shapes);
+
+ // To fill the index cell we merge the two sources of shapes: "edge shapes"
+ // (those that have at least one edge that intersects this cell), and
+ // "containing shapes" (those that contain the cell center). We keep track
+ // of the index of the next intersecting edge and the next containing shape
+ // as we go along. Both sets of shape ids are already sorted.
+ int enext = 0;
+ ShapeIdSet::const_iterator cnext = cshape_ids.begin();
+ for (int i = 0; i < num_shapes; ++i) {
+ S2ClippedShape* clipped = base + i;
+ int eshape_id = num_shape_ids(), cshape_id = eshape_id; // Sentinels
+ if (enext != edges.size()) {
+ eshape_id = edges[enext]->face_edge->shape_id;
+ }
+ if (cnext != cshape_ids.end()) {
+ cshape_id = *cnext;
+ }
+ int ebegin = enext;
+ if (cshape_id < eshape_id) {
+ // The entire cell is in the shape interior.
+ clipped->Init(cshape_id, 0);
+ clipped->set_contains_center(true);
+ ++cnext;
+ } else {
+ // Count the number of edges for this shape and allocate space for them.
+ while (enext < edges.size() &&
+ edges[enext]->face_edge->shape_id == eshape_id) {
+ ++enext;
+ }
+ clipped->Init(eshape_id, enext - ebegin);
+ for (int e = ebegin; e < enext; ++e) {
+ clipped->set_edge(e - ebegin, edges[e]->face_edge->edge_id);
+ }
+ if (cshape_id == eshape_id) {
+ clipped->set_contains_center(true);
+ ++cnext;
+ }
+ }
+ }
+ // UpdateEdges() visits cells in increasing order of S2CellId, so during
+ // initial construction of the index all insertions happen at the end. It
+ // is much faster to give an insertion hint in this case. Otherwise the
+ // hint doesn't do much harm. With more effort we could provide a hint even
+ // during incremental updates, but this is probably not worth the effort.
+ cell_map_.insert(cell_map_.end(), std::make_pair(pcell.id(), cell));
+
+ // Shift the InteriorTracker focus point to the exit vertex of this cell.
+ if (tracker->is_active() && !edges.empty()) {
+ tracker->DrawTo(pcell.GetExitVertex());
+ TestAllEdges(edges, tracker);
+ tracker->set_next_cellid(pcell.id().next());
+ }
+ return true;
+}
+
+// Call tracker->TestEdge() on all edges from shapes that have interiors.
+/* static */
+void MutableS2ShapeIndex::TestAllEdges(const vector<const ClippedEdge*>& edges,
+ InteriorTracker* tracker) {
+ for (const ClippedEdge* edge : edges) {
+ const FaceEdge* face_edge = edge->face_edge;
+ if (face_edge->has_interior) {
+ tracker->TestEdge(face_edge->shape_id, face_edge->edge);
+ }
+ }
+}
+
+// Return the number of distinct shapes that are either associated with the
+// given edges, or that are currently stored in the InteriorTracker.
+/* static */
+int MutableS2ShapeIndex::CountShapes(const vector<const ClippedEdge*>& edges,
+ const ShapeIdSet& cshape_ids) {
+ int count = 0;
+ int last_shape_id = -1;
+ ShapeIdSet::const_iterator cnext = cshape_ids.begin(); // Next shape
+ for (const ClippedEdge* edge : edges) {
+ if (edge->face_edge->shape_id != last_shape_id) {
+ ++count;
+ last_shape_id = edge->face_edge->shape_id;
+ // Skip over any containing shapes up to and including this one,
+ // updating "count" appropriately.
+ for (; cnext != cshape_ids.end(); ++cnext) {
+ if (*cnext > last_shape_id) break;
+ if (*cnext < last_shape_id) ++count;
+ }
+ }
+ }
+ // Count any remaining containing shapes.
+ count += (cshape_ids.end() - cnext);
+ return count;
+}
+
+size_t MutableS2ShapeIndex::SpaceUsed() const {
+ size_t size = sizeof(*this);
+ size += shapes_.capacity() * sizeof(std::unique_ptr<S2Shape>);
+ // cell_map_ itself is already included in sizeof(*this).
+ size += cell_map_.bytes_used() - sizeof(cell_map_);
+ size += cell_map_.size() * sizeof(S2ShapeIndexCell);
+ Iterator it;
+ for (it.InitStale(this, S2ShapeIndex::BEGIN); !it.done(); it.Next()) {
+ const S2ShapeIndexCell& cell = it.cell();
+ size += cell.shapes_.capacity() * sizeof(S2ClippedShape);
+ for (int s = 0; s < cell.num_clipped(); ++s) {
+ const S2ClippedShape& clipped = cell.clipped(s);
+ if (!clipped.is_inline()) {
+ size += clipped.num_edges() * sizeof(int32);
+ }
+ }
+ }
+ if (pending_removals_ != nullptr) {
+ size += pending_removals_->capacity() * sizeof(RemovedShape);
+ }
+
+ return size;
+}
+
+void MutableS2ShapeIndex::Encode(Encoder* encoder) const {
+ // The version number is encoded in 2 bits, under the assumption that by the
+ // time we need 5 versions the first version can be permanently retired.
+ // This only saves 1 byte, but that's significant for very small indexes.
+ encoder->Ensure(Varint::kMax64);
+ uint64 max_edges = options_.max_edges_per_cell();
+ encoder->put_varint64(max_edges << 2 | kCurrentEncodingVersionNumber);
+
+ vector<S2CellId> cell_ids;
+ cell_ids.reserve(cell_map_.size());
+ s2coding::StringVectorEncoder encoded_cells;
+ for (Iterator it(this, S2ShapeIndex::BEGIN); !it.done(); it.Next()) {
+ cell_ids.push_back(it.id());
+ it.cell().Encode(num_shape_ids(), encoded_cells.AddViaEncoder());
+ }
+ s2coding::EncodeS2CellIdVector(cell_ids, encoder);
+ encoded_cells.Encode(encoder);
+}
+
+bool MutableS2ShapeIndex::Init(Decoder* decoder,
+ const ShapeFactory& shape_factory) {
+ Clear();
+ uint64 max_edges_version;
+ if (!decoder->get_varint64(&max_edges_version)) return false;
+ int version = max_edges_version & 3;
+ if (version != kCurrentEncodingVersionNumber) return false;
+ options_.set_max_edges_per_cell(max_edges_version >> 2);
+ uint32 num_shapes = shape_factory.size();
+ shapes_.reserve(num_shapes);
+ for (int shape_id = 0; shape_id < num_shapes; ++shape_id) {
+ auto shape = shape_factory[shape_id];
+ if (shape) shape->id_ = shape_id;
+ shapes_.push_back(std::move(shape));
+ }
+
+ s2coding::EncodedS2CellIdVector cell_ids;
+ s2coding::EncodedStringVector encoded_cells;
+ if (!cell_ids.Init(decoder)) return false;
+ if (!encoded_cells.Init(decoder)) return false;
+
+ for (int i = 0; i < cell_ids.size(); ++i) {
+ S2CellId id = cell_ids[i];
+ S2ShapeIndexCell* cell = new S2ShapeIndexCell;
+ Decoder decoder = encoded_cells.GetDecoder(i);
+ if (!cell->Decode(num_shapes, &decoder)) return false;
+ cell_map_.insert(cell_map_.end(), std::make_pair(id, cell));
+ }
+ return true;
+}
--- /dev/null
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/r2rect.h"
+
+#include <iosfwd>
+
+#include "s2/base/logging.h"
+#include "s2/r1interval.h"
+#include "s2/r2.h"
+
+R2Rect R2Rect::FromCenterSize(const R2Point& center, const R2Point& size) {
+ return R2Rect(R1Interval(center.x() - 0.5 * size.x(),
+ center.x() + 0.5 * size.x()),
+ R1Interval(center.y() - 0.5 * size.y(),
+ center.y() + 0.5 * size.y()));
+}
+
+R2Rect R2Rect::FromPointPair(const R2Point& p1, const R2Point& p2) {
+ return R2Rect(R1Interval::FromPointPair(p1.x(), p2.x()),
+ R1Interval::FromPointPair(p1.y(), p2.y()));
+}
+
+bool R2Rect::Contains(const R2Rect& other) const {
+ return x().Contains(other.x()) && y().Contains(other.y());
+}
+
+bool R2Rect::InteriorContains(const R2Rect& other) const {
+ return x().InteriorContains(other.x()) && y().InteriorContains(other.y());
+}
+
+bool R2Rect::Intersects(const R2Rect& other) const {
+ return x().Intersects(other.x()) && y().Intersects(other.y());
+}
+
+bool R2Rect::InteriorIntersects(const R2Rect& other) const {
+ return x().InteriorIntersects(other.x()) && y().InteriorIntersects(other.y());
+}
+
+void R2Rect::AddPoint(const R2Point& p) {
+ bounds_[0].AddPoint(p[0]);
+ bounds_[1].AddPoint(p[1]);
+}
+
+void R2Rect::AddRect(const R2Rect& other) {
+ bounds_[0].AddInterval(other[0]);
+ bounds_[1].AddInterval(other[1]);
+}
+
+R2Point R2Rect::Project(const R2Point& p) const {
+ return R2Point(x().Project(p.x()), y().Project(p.y()));
+}
+
+R2Rect R2Rect::Expanded(const R2Point& margin) const {
+ R1Interval xx = x().Expanded(margin.x());
+ R1Interval yy = y().Expanded(margin.y());
+ if (xx.is_empty() || yy.is_empty()) return Empty();
+ return R2Rect(xx, yy);
+}
+
+R2Rect R2Rect::Union(const R2Rect& other) const {
+ return R2Rect(x().Union(other.x()), y().Union(other.y()));
+}
+
+R2Rect R2Rect::Intersection(const R2Rect& other) const {
+ R1Interval xx = x().Intersection(other.x());
+ R1Interval yy = y().Intersection(other.y());
+ if (xx.is_empty() || yy.is_empty()) return Empty();
+ return R2Rect(xx, yy);
+}
+
+bool R2Rect::ApproxEquals(const R2Rect& other, double max_error) const {
+ return (x().ApproxEquals(other.x(), max_error) &&
+ y().ApproxEquals(other.y(), max_error));
+}
+
+std::ostream& operator<<(std::ostream& os, const R2Rect& r) {
+ return os << "[Lo" << r.lo() << ", Hi" << r.hi() << "]";
+}
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s1angle.h"
+
+#include <cmath>
+#include <cstdio>
+#include <ostream>
+
+#include "s2/s2latlng.h"
+
+S1Angle::S1Angle(const S2Point& x, const S2Point& y)
+ : radians_(x.Angle(y)) {
+}
+
+S1Angle::S1Angle(const S2LatLng& x, const S2LatLng& y)
+ : radians_(x.GetDistance(y).radians()) {
+}
+
+S1Angle S1Angle::Normalized() const {
+ S1Angle a(radians_);
+ a.Normalize();
+ return a;
+}
+
+void S1Angle::Normalize() {
+ radians_ = remainder(radians_, 2.0 * M_PI);
+ if (radians_ <= -M_PI) radians_ = M_PI;
+}
+
+std::ostream& operator<<(std::ostream& os, S1Angle a) {
+ double degrees = a.degrees();
+ char buffer[13];
+ int sz = snprintf(buffer, sizeof(buffer), "%.7f", degrees);
+ if (sz >= 0 && sz < sizeof(buffer)) {
+ return os << buffer;
+ } else {
+ return os << degrees;
+ }
+}
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s1chord_angle.h"
+
+#include <cfloat>
+#include <cmath>
+
+#include "s2/s1angle.h"
+#include "s2/s2pointutil.h"
+
+using std::max;
+using std::min;
+// Android with gnustl has ::nextafter but not std::nextafter.
+// https://github.com/android-ndk/ndk/issues/82
+// Check for gnustl with _GLIBCXX_CMATH, which is its cmath include
+// guard.
+#if !defined(__ANDROID__) || !defined(_GLIBCXX_CMATH)
+using std::nextafter;
+#endif
+
+static constexpr double kMaxLength2 = 4.0;
+
+S1ChordAngle::S1ChordAngle(S1Angle angle) {
+ if (angle.radians() < 0) {
+ *this = Negative();
+ } else if (angle == S1Angle::Infinity()) {
+ *this = Infinity();
+ } else {
+ // The chord length is 2 * sin(angle / 2).
+ double length = 2 * sin(0.5 * min(M_PI, angle.radians()));
+ length2_ = length * length;
+ }
+ S2_DCHECK(is_valid());
+}
+
+S1Angle S1ChordAngle::ToAngle() const {
+ if (is_negative()) return S1Angle::Radians(-1);
+ if (is_infinity()) return S1Angle::Infinity();
+ return S1Angle::Radians(2 * asin(0.5 * sqrt(length2_)));
+}
+
+bool S1ChordAngle::is_valid() const {
+ return (length2_ >= 0 && length2_ <= kMaxLength2) || is_special();
+}
+
+S1ChordAngle S1ChordAngle::Successor() const {
+ if (length2_ >= kMaxLength2) return Infinity();
+ if (length2_ < 0.0) return Zero();
+ return S1ChordAngle(nextafter(length2_, 10.0));
+}
+
+S1ChordAngle S1ChordAngle::Predecessor() const {
+ if (length2_ <= 0.0) return Negative();
+ if (length2_ > kMaxLength2) return Straight();
+ return S1ChordAngle(nextafter(length2_, -10.0));
+}
+
+S1ChordAngle S1ChordAngle::PlusError(double error) const {
+ // If angle is Negative() or Infinity(), don't change it.
+ // Otherwise clamp it to the valid range.
+ if (is_special()) return *this;
+ return S1ChordAngle(max(0.0, min(kMaxLength2, length2_ + error)));
+}
+
+double S1ChordAngle::GetS2PointConstructorMaxError() const {
+ // There is a relative error of 2.5 * DBL_EPSILON when computing the squared
+ // distance, plus a relative error of 2 * DBL_EPSILON and an absolute error
+ // of (16 * DBL_EPSILON**2) because the lengths of the input points may
+ // differ from 1 by up to (2 * DBL_EPSILON) each. (This is the maximum
+ // length error in S2Point::Normalize.)
+ return 4.5 * DBL_EPSILON * length2_ + 16 * DBL_EPSILON * DBL_EPSILON;
+}
+
+double S1ChordAngle::GetS1AngleConstructorMaxError() const {
+ // Assuming that an accurate math library is being used, the sin() call and
+ // the multiply each have a relative error of 0.5 * DBL_EPSILON.
+ return DBL_EPSILON * length2_;
+}
+
+S1ChordAngle operator+(S1ChordAngle a, S1ChordAngle b) {
+ // Note that this method is much more efficient than converting the chord
+ // angles to S1Angles and adding those. It requires only one square root
+ // plus a few additions and multiplications.
+ S2_DCHECK(!a.is_special());
+ S2_DCHECK(!b.is_special());
+
+ // Optimization for the common case where "b" is an error tolerance
+ // parameter that happens to be set to zero.
+ double a2 = a.length2(), b2 = b.length2();
+ if (b2 == 0) return a;
+
+ // Clamp the angle sum to at most 180 degrees.
+ if (a2 + b2 >= kMaxLength2) return S1ChordAngle::Straight();
+
+ // Let "a" and "b" be the (non-squared) chord lengths, and let c = a+b.
+ // Let A, B, and C be the corresponding half-angles (a = 2*sin(A), etc).
+ // Then the formula below can be derived from c = 2 * sin(A+B) and the
+ // relationships sin(A+B) = sin(A)*cos(B) + sin(B)*cos(A)
+ // cos(X) = sqrt(1 - sin^2(X)) .
+
+ double x = a2 * (1 - 0.25 * b2); // is_valid() => non-negative
+ double y = b2 * (1 - 0.25 * a2); // is_valid() => non-negative
+ return S1ChordAngle(min(kMaxLength2, x + y + 2 * sqrt(x * y)));
+}
+
+S1ChordAngle operator-(S1ChordAngle a, S1ChordAngle b) {
+ // See comments in operator+().
+ S2_DCHECK(!a.is_special());
+ S2_DCHECK(!b.is_special());
+ double a2 = a.length2(), b2 = b.length2();
+ if (b2 == 0) return a;
+ if (a2 <= b2) return S1ChordAngle::Zero();
+ double x = a2 * (1 - 0.25 * b2);
+ double y = b2 * (1 - 0.25 * a2);
+ return S1ChordAngle(max(0.0, x + y - 2 * sqrt(x * y)));
+}
+
+double sin2(S1ChordAngle a) {
+ S2_DCHECK(!a.is_special());
+ // Let "a" be the (non-squared) chord length, and let A be the corresponding
+ // half-angle (a = 2*sin(A)). The formula below can be derived from:
+ // sin(2*A) = 2 * sin(A) * cos(A)
+ // cos^2(A) = 1 - sin^2(A)
+ // This is much faster than converting to an angle and computing its sine.
+ return a.length2() * (1 - 0.25 * a.length2());
+}
+
+double sin(S1ChordAngle a) {
+ return sqrt(sin2(a));
+}
+
+double cos(S1ChordAngle a) {
+ // cos(2*A) = cos^2(A) - sin^2(A) = 1 - 2*sin^2(A)
+ S2_DCHECK(!a.is_special());
+ return 1 - 0.5 * a.length2();
+}
+
+double tan(S1ChordAngle a) {
+ return sin(a) / cos(a);
+}
+
+std::ostream& operator<<(std::ostream& os, S1ChordAngle a) {
+ return os << a.ToAngle();
+}
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s1interval.h"
+
+#include <algorithm>
+#include <cfloat>
+#include <cmath>
+
+#include "s2/base/logging.h"
+
+using std::fabs;
+using std::max;
+
+S1Interval S1Interval::FromPoint(double p) {
+ if (p == -M_PI) p = M_PI;
+ return S1Interval(p, p, ARGS_CHECKED);
+}
+
+double S1Interval::GetCenter() const {
+ double center = 0.5 * (lo() + hi());
+ if (!is_inverted()) return center;
+ // Return the center in the range (-Pi, Pi].
+ return (center <= 0) ? (center + M_PI) : (center - M_PI);
+}
+
+double S1Interval::GetLength() const {
+ double length = hi() - lo();
+ if (length >= 0) return length;
+ length += 2 * M_PI;
+ // Empty intervals have a negative length.
+ return (length > 0) ? length : -1;
+}
+
+S1Interval S1Interval::Complement() const {
+ if (lo() == hi()) return Full(); // Singleton.
+ return S1Interval(hi(), lo(), ARGS_CHECKED); // Handles empty and full.
+}
+
+double S1Interval::GetComplementCenter() const {
+ if (lo() != hi()) {
+ return Complement().GetCenter();
+ } else { // Singleton.
+ return (hi() <= 0) ? (hi() + M_PI) : (hi() - M_PI);
+ }
+}
+
+bool S1Interval::FastContains(double p) const {
+ if (is_inverted()) {
+ return (p >= lo() || p <= hi()) && !is_empty();
+ } else {
+ return p >= lo() && p <= hi();
+ }
+}
+
+bool S1Interval::Contains(double p) const {
+ // Works for empty, full, and singleton intervals.
+ S2_DCHECK_LE(fabs(p), M_PI);
+ if (p == -M_PI) p = M_PI;
+ return FastContains(p);
+}
+
+bool S1Interval::InteriorContains(double p) const {
+ // Works for empty, full, and singleton intervals.
+ S2_DCHECK_LE(fabs(p), M_PI);
+ if (p == -M_PI) p = M_PI;
+
+ if (is_inverted()) {
+ return p > lo() || p < hi();
+ } else {
+ return (p > lo() && p < hi()) || is_full();
+ }
+}
+
+bool S1Interval::Contains(const S1Interval& y) const {
+ // It might be helpful to compare the structure of these tests to
+ // the simpler Contains(double) method above.
+
+ if (is_inverted()) {
+ if (y.is_inverted()) return y.lo() >= lo() && y.hi() <= hi();
+ return (y.lo() >= lo() || y.hi() <= hi()) && !is_empty();
+ } else {
+ if (y.is_inverted()) return is_full() || y.is_empty();
+ return y.lo() >= lo() && y.hi() <= hi();
+ }
+}
+
+bool S1Interval::InteriorContains(const S1Interval& y) const {
+ if (is_inverted()) {
+ if (!y.is_inverted()) return y.lo() > lo() || y.hi() < hi();
+ return (y.lo() > lo() && y.hi() < hi()) || y.is_empty();
+ } else {
+ if (y.is_inverted()) return is_full() || y.is_empty();
+ return (y.lo() > lo() && y.hi() < hi()) || is_full();
+ }
+}
+
+bool S1Interval::Intersects(const S1Interval& y) const {
+ if (is_empty() || y.is_empty()) return false;
+ if (is_inverted()) {
+ // Every non-empty inverted interval contains Pi.
+ return y.is_inverted() || y.lo() <= hi() || y.hi() >= lo();
+ } else {
+ if (y.is_inverted()) return y.lo() <= hi() || y.hi() >= lo();
+ return y.lo() <= hi() && y.hi() >= lo();
+ }
+}
+
+bool S1Interval::InteriorIntersects(const S1Interval& y) const {
+ if (is_empty() || y.is_empty() || lo() == hi()) return false;
+ if (is_inverted()) {
+ return y.is_inverted() || y.lo() < hi() || y.hi() > lo();
+ } else {
+ if (y.is_inverted()) return y.lo() < hi() || y.hi() > lo();
+ return (y.lo() < hi() && y.hi() > lo()) || is_full();
+ }
+}
+
+inline static double PositiveDistance(double a, double b) {
+ // Compute the distance from "a" to "b" in the range [0, 2*Pi).
+ // This is equivalent to (remainder(b - a - M_PI, 2 * M_PI) + M_PI),
+ // except that it is more numerically stable (it does not lose
+ // precision for very small positive distances).
+ double d = b - a;
+ if (d >= 0) return d;
+ // We want to ensure that if b == Pi and a == (-Pi + eps),
+ // the return result is approximately 2*Pi and not zero.
+ return (b + M_PI) - (a - M_PI);
+}
+
+double S1Interval::GetDirectedHausdorffDistance(const S1Interval& y) const {
+ if (y.Contains(*this)) return 0.0; // this includes the case *this is empty
+ if (y.is_empty()) return M_PI; // maximum possible distance on S1
+
+ double y_complement_center = y.GetComplementCenter();
+ if (Contains(y_complement_center)) {
+ return PositiveDistance(y.hi(), y_complement_center);
+ } else {
+ // The Hausdorff distance is realized by either two hi() endpoints or two
+ // lo() endpoints, whichever is farther apart.
+ double hi_hi = S1Interval(y.hi(), y_complement_center).Contains(hi()) ?
+ PositiveDistance(y.hi(), hi()) : 0;
+ double lo_lo = S1Interval(y_complement_center, y.lo()).Contains(lo()) ?
+ PositiveDistance(lo(), y.lo()) : 0;
+ S2_DCHECK(hi_hi > 0 || lo_lo > 0);
+ return max(hi_hi, lo_lo);
+ }
+}
+
+void S1Interval::AddPoint(double p) {
+ S2_DCHECK_LE(fabs(p), M_PI);
+ if (p == -M_PI) p = M_PI;
+
+ if (FastContains(p)) return;
+ if (is_empty()) {
+ set_hi(p);
+ set_lo(p);
+ } else {
+ // Compute distance from p to each endpoint.
+ double dlo = PositiveDistance(p, lo());
+ double dhi = PositiveDistance(hi(), p);
+ if (dlo < dhi) {
+ set_lo(p);
+ } else {
+ set_hi(p);
+ }
+ // Adding a point can never turn a non-full interval into a full one.
+ }
+}
+
+double S1Interval::Project(double p) const {
+ S2_DCHECK(!is_empty());
+ S2_DCHECK_LE(fabs(p), M_PI);
+ if (p == -M_PI) p = M_PI;
+ if (FastContains(p)) return p;
+ // Compute distance from p to each endpoint.
+ double dlo = PositiveDistance(p, lo());
+ double dhi = PositiveDistance(hi(), p);
+ return (dlo < dhi) ? lo() : hi();
+}
+
+S1Interval S1Interval::FromPointPair(double p1, double p2) {
+ S2_DCHECK_LE(fabs(p1), M_PI);
+ S2_DCHECK_LE(fabs(p2), M_PI);
+ if (p1 == -M_PI) p1 = M_PI;
+ if (p2 == -M_PI) p2 = M_PI;
+ if (PositiveDistance(p1, p2) <= M_PI) {
+ return S1Interval(p1, p2, ARGS_CHECKED);
+ } else {
+ return S1Interval(p2, p1, ARGS_CHECKED);
+ }
+}
+
+S1Interval S1Interval::Expanded(double margin) const {
+ if (margin >= 0) {
+ if (is_empty()) return *this;
+ // Check whether this interval will be full after expansion, allowing
+ // for a 1-bit rounding error when computing each endpoint.
+ if (GetLength() + 2 * margin + 2 * DBL_EPSILON >= 2 * M_PI) return Full();
+ } else {
+ if (is_full()) return *this;
+ // Check whether this interval will be empty after expansion, allowing
+ // for a 1-bit rounding error when computing each endpoint.
+ if (GetLength() + 2 * margin - 2 * DBL_EPSILON <= 0) return Empty();
+ }
+ S1Interval result(remainder(lo() - margin, 2*M_PI),
+ remainder(hi() + margin, 2*M_PI));
+ if (result.lo() <= -M_PI) result.set_lo(M_PI);
+ return result;
+}
+
+S1Interval S1Interval::Union(const S1Interval& y) const {
+ // The y.is_full() case is handled correctly in all cases by the code
+ // below, but can follow three separate code paths depending on whether
+ // this interval is inverted, is non-inverted but contains Pi, or neither.
+
+ if (y.is_empty()) return *this;
+ if (FastContains(y.lo())) {
+ if (FastContains(y.hi())) {
+ // Either this interval contains y, or the union of the two
+ // intervals is the Full() interval.
+ if (Contains(y)) return *this; // is_full() code path
+ return Full();
+ }
+ return S1Interval(lo(), y.hi(), ARGS_CHECKED);
+ }
+ if (FastContains(y.hi())) return S1Interval(y.lo(), hi(), ARGS_CHECKED);
+
+ // This interval contains neither endpoint of y. This means that either y
+ // contains all of this interval, or the two intervals are disjoint.
+ if (is_empty() || y.FastContains(lo())) return y;
+
+ // Check which pair of endpoints are closer together.
+ double dlo = PositiveDistance(y.hi(), lo());
+ double dhi = PositiveDistance(hi(), y.lo());
+ if (dlo < dhi) {
+ return S1Interval(y.lo(), hi(), ARGS_CHECKED);
+ } else {
+ return S1Interval(lo(), y.hi(), ARGS_CHECKED);
+ }
+}
+
+S1Interval S1Interval::Intersection(const S1Interval& y) const {
+ // The y.is_full() case is handled correctly in all cases by the code
+ // below, but can follow three separate code paths depending on whether
+ // this interval is inverted, is non-inverted but contains Pi, or neither.
+
+ if (y.is_empty()) return Empty();
+ if (FastContains(y.lo())) {
+ if (FastContains(y.hi())) {
+ // Either this interval contains y, or the region of intersection
+ // consists of two disjoint subintervals. In either case, we want
+ // to return the shorter of the two original intervals.
+ if (y.GetLength() < GetLength()) return y; // is_full() code path
+ return *this;
+ }
+ return S1Interval(y.lo(), hi(), ARGS_CHECKED);
+ }
+ if (FastContains(y.hi())) return S1Interval(lo(), y.hi(), ARGS_CHECKED);
+
+ // This interval contains neither endpoint of y. This means that either y
+ // contains all of this interval, or the two intervals are disjoint.
+
+ if (y.FastContains(lo())) return *this; // is_empty() okay here
+ S2_DCHECK(!Intersects(y));
+ return Empty();
+}
+
+bool S1Interval::ApproxEquals(const S1Interval& y, double max_error) const {
+ // Full and empty intervals require special cases because the "endpoints"
+ // are considered to be positioned arbitrarily.
+ if (is_empty()) return y.GetLength() <= 2 * max_error;
+ if (y.is_empty()) return GetLength() <= 2 * max_error;
+ if (is_full()) return y.GetLength() >= 2 * (M_PI - max_error);
+ if (y.is_full()) return GetLength() >= 2 * (M_PI - max_error);
+
+ // The purpose of the last test below is to verify that moving the endpoints
+ // does not invert the interval, e.g. [-1e20, 1e20] vs. [1e20, -1e20].
+ return (fabs(remainder(y.lo() - lo(), 2 * M_PI)) <= max_error &&
+ fabs(remainder(y.hi() - hi(), 2 * M_PI)) <= max_error &&
+ fabs(GetLength() - y.GetLength()) <= 2 * max_error);
+}
--- /dev/null
+#include "cpp-compat.h"
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// Boolean operations are implemented by constructing the boundary of the
+// result and then using S2Builder to assemble the edges. The boundary is
+// obtained by clipping each of the two input regions to the interior or
+// exterior of the other region. For example, to compute the union of A and
+// B, we clip the boundary of A to the exterior of B and the boundary of B to
+// the exterior of A; the resulting set of edges defines the union of the two
+// regions.
+//
+// We use exact predicates, but inexact constructions (e.g. computing the
+// intersection point of two edges). Nevertheless, the following algorithm is
+// guaranteed to be 100% robust, in that the computed boundary stays within a
+// small tolerance (snap_radius + S2::kIntersectionError) of the exact
+// result, and also preserves the correct topology (i.e., no crossing edges).
+//
+// Unfortunately this robustness cannot quite be achieved using the strategy
+// outlined above (clipping the two input regions and assembling the
+// resulting edges). Since computed intersection points are not exact, the
+// input geometry passed to S2Builder might contain self-intersections, and
+// these self-intersections cannot be eliminated reliably by snap rounding.
+//
+// So instead, we pass S2Builder the entire set of input edges where at least
+// some portion of each edge belongs to the output boundary. We allow
+// S2Builder to compute the intersection points and snap round the edges
+// (which it does in a way that is guaranteed to preserve the input topology).
+// Then once this is finished, we remove the portions of each edge that would
+// have been clipped if we had done the clipping first. This step only
+// involves deciding whether to keep or discard each edge in the output, since
+// all intersection points have already been resolved, and therefore there is
+// no risk of creating new self-intersections.
+//
+// This is implemented using the following classes:
+//
+// - S2BooleanOperation::Impl: the top-level class that clips each of
+// the two regions to the other region.
+//
+// - CrossingProcessor: a class that processes edge crossings and maintains
+// the necessary state in order to clip the boundary
+// of one region to the interior or exterior of the
+// other region.
+//
+// - EdgeClippingLayer: an S2Builder::Layer that removes graph edges that
+// correspond to clipped portions of input edges, and
+// passes the result to another layer for assembly.
+//
+// - GraphEdgeClipper: a helper class that does the actual work of the
+// EdgeClippingLayer.
+
+#include "s2/s2boolean_operation.h"
+
+#include <algorithm>
+#include <limits>
+#include <memory>
+#include <utility>
+
+#include "s2/util/gtl/btree_map.h"
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/s2builder.h"
+#include "s2/s2builder_layer.h"
+#include "s2/s2builderutil_snap_functions.h"
+#include "s2/s2contains_point_query.h"
+#include "s2/s2crossing_edge_query.h"
+#include "s2/s2edge_crosser.h"
+#include "s2/s2edge_crossings.h"
+#include "s2/s2measures.h"
+#include "s2/s2predicates.h"
+#include "s2/s2shape_index_measures.h"
+#include "s2/s2shapeutil_visit_crossing_edge_pairs.h"
+
+// TODO(ericv): Remove this debugging output at some point.
+extern bool s2builder_verbose;
+
+namespace { // Anonymous namespace for helper classes.
+
+using absl::make_unique;
+using std::make_pair;
+using std::max;
+using std::min;
+using std::pair;
+using std::swap;
+using std::unique_ptr;
+using std::vector;
+
+using EdgeType = S2Builder::EdgeType;
+using SnapFunction = S2Builder::SnapFunction;
+using GraphOptions = S2Builder::GraphOptions;
+using DegenerateEdges = GraphOptions::DegenerateEdges;
+using DuplicateEdges = GraphOptions::DuplicateEdges;
+using SiblingPairs = GraphOptions::SiblingPairs;
+
+using Graph = S2Builder::Graph;
+using EdgeId = Graph::EdgeId;
+using VertexId = Graph::VertexId;
+using InputEdgeId = Graph::InputEdgeId;
+using InputEdgeIdSetId = Graph::InputEdgeIdSetId;
+
+using PolygonModel = S2BooleanOperation::PolygonModel;
+using PolylineModel = S2BooleanOperation::PolylineModel;
+using Precision = S2BooleanOperation::Precision;
+
+// A collection of special InputEdgeIds that allow the GraphEdgeClipper state
+// modifications to be inserted into the list of edge crossings.
+static const InputEdgeId kSetInside = -1;
+static const InputEdgeId kSetInvertB = -2;
+static const InputEdgeId kSetReverseA = -3;
+
+// CrossingInputEdge represents an input edge B that crosses some other input
+// edge A. It stores the input edge id of edge B and also whether it crosses
+// edge A from left to right (or vice versa).
+class CrossingInputEdge {
+ public:
+ // Indicates that input edge "input_id" crosses another edge (from left to
+ // right if "left_to_right" is true).
+ CrossingInputEdge(InputEdgeId input_id, bool left_to_right)
+ : left_to_right_(left_to_right), input_id_(input_id) {
+ }
+
+ InputEdgeId input_id() const { return input_id_; }
+ bool left_to_right() const { return left_to_right_; }
+
+ bool operator<(const CrossingInputEdge& other) const {
+ return input_id_ < other.input_id_;
+ }
+ bool operator<(const InputEdgeId& other) const {
+ return input_id_ < other;
+ }
+
+ private:
+ bool left_to_right_ : 1;
+ InputEdgeId input_id_ : 31;
+};
+
+// InputEdgeCrossings represents all pairs of intersecting input edges.
+// It is sorted in lexicographic order.
+using InputEdgeCrossings = vector<pair<InputEdgeId, CrossingInputEdge>>;
+
+// Given two input edges A and B that intersect, suppose that A maps to a
+// chain of snapped edges A_0, A_1, ..., A_m and B maps to a chain of snapped
+// edges B_0, B_1, ..., B_n. CrossingGraphEdge represents an edge from chain
+// B that shares a vertex with chain A. It is used as a temporary data
+// representation while processing chain A. The arguments are:
+//
+// "id" - the Graph::EdgeId of an edge from chain B.
+// "a_index" - the index of the vertex (A_i) that is shared with chain A.
+// "outgoing" - true if the shared vertex is the first vertex of the B edge.
+// "dst" - the Graph::VertexId of the vertex that is not shared with chain A.
+//
+// Note that if an edge from the B chain shares both vertices with the A
+// chain, there will be two entries: an outgoing edge that treats its first
+// vertex as being shared, and an incoming edge that treats its second vertex
+// as being shared.
+struct CrossingGraphEdge {
+ CrossingGraphEdge(EdgeId _id, int _a_index, bool _outgoing, VertexId _dst)
+ : id(_id), a_index(_a_index), outgoing(_outgoing), dst(_dst) {
+ }
+ EdgeId id;
+ int a_index;
+ bool outgoing;
+ VertexId dst;
+};
+using CrossingGraphEdgeVector = absl::InlinedVector<CrossingGraphEdge, 2>;
+
+// Returns a vector of EdgeIds sorted by input edge id. When more than one
+// output edge has the same input edge id (i.e., the input edge snapped to a
+// chain of edges), the edges are sorted so that they form a directed edge
+// chain.
+//
+// This function could possibily be moved to S2Builder::Graph, but note that
+// it has special requirements. Namely, duplicate edges and sibling pairs
+// must be kept in order to ensure that every output edge corresponds to
+// exactly one input edge. (See also S2Builder::Graph::GetInputEdgeOrder.)
+static vector<EdgeId> GetInputEdgeChainOrder(
+ const Graph& g, const vector<InputEdgeId>& input_ids) {
+
+ S2_DCHECK(g.options().edge_type() == EdgeType::DIRECTED);
+ S2_DCHECK(g.options().duplicate_edges() == DuplicateEdges::KEEP);
+ S2_DCHECK(g.options().sibling_pairs() == SiblingPairs::KEEP);
+
+ // First, sort the edges so that the edges corresponding to each input edge
+ // are consecutive. (Each input edge was snapped to a chain of output
+ // edges, or two chains in the case of undirected input edges.)
+ vector<EdgeId> order = g.GetInputEdgeOrder(input_ids);
+
+ // Now sort the group of edges corresponding to each input edge in edge
+ // chain order (e.g. AB, BC, CD).
+ vector<pair<VertexId, EdgeId>> vmap; // Map from source vertex to edge id.
+ vector<int> indegree(g.num_vertices()); // Restricted to current input edge.
+ for (int end, begin = 0; begin < order.size(); begin = end) {
+ // Gather the edges that came from a single input edge.
+ InputEdgeId input_id = input_ids[order[begin]];
+ for (end = begin; end < order.size(); ++end) {
+ if (input_ids[order[end]] != input_id) break;
+ }
+ if (end - begin == 1) continue;
+
+ // Build a map from the source vertex of each edge to its edge id,
+ // and also compute the indegree at each vertex considering only the edges
+ // that came from the current input edge.
+ for (int i = begin; i < end; ++i) {
+ EdgeId e = order[i];
+ vmap.push_back(make_pair(g.edge(e).first, e));
+ indegree[g.edge(e).second] += 1;
+ }
+ std::sort(vmap.begin(), vmap.end());
+
+ // Find the starting edge for building the edge chain.
+ EdgeId next = g.num_edges();
+ for (int i = begin; i < end; ++i) {
+ EdgeId e = order[i];
+ if (indegree[g.edge(e).first] == 0) next = e;
+ }
+ // Build the edge chain.
+ for (int i = begin; ;) {
+ order[i] = next;
+ VertexId v = g.edge(next).second;
+ indegree[v] = 0; // Clear as we go along.
+ if (++i == end) break;
+ auto out = lower_bound(vmap.begin(), vmap.end(), make_pair(v, 0));
+ S2_DCHECK_EQ(v, out->first);
+ next = out->second;
+ }
+ vmap.clear();
+ }
+ return order;
+}
+
+// Given a set of clipping instructions encoded as a set of InputEdgeCrossings,
+// GraphEdgeClipper determines which graph edges correspond to clipped
+// portions of input edges and removes them.
+//
+// The clipping model is as follows. The input consists of edge chains. The
+// clipper maintains an "inside" boolean state as it clips each chain, and
+// toggles this state whenever an input edge is crossed. Any edges that are
+// deemed to be "outside" after clipping are removed.
+//
+// The "inside" state can be reset when necessary (e.g., when jumping to the
+// start of a new chain) by adding a special crossing marked kSetInside.
+// There are also two other special "crossings" that modify the clipping
+// parameters: kSetInvertB specifies that edges should be clipped to the
+// exterior of the other region, and kSetReverseA specifies that edges should
+// be reversed before emitting them (which is needed to implement difference
+// operations).
+class GraphEdgeClipper {
+ public:
+ // "input_dimensions" is a vector specifying the dimension of each input
+ // edge (0, 1, or 2). "input_crossings" is the set of all crossings to be
+ // used when clipping the edges of "g", sorted in lexicographic order.
+ //
+ // The clipped set of edges and their corresponding set of input edge ids
+ // are returned in "new_edges" and "new_input_edge_ids". (These can be used
+ // to construct a new S2Builder::Graph.)
+ GraphEdgeClipper(const Graph& g, const vector<int8>& input_dimensions,
+ const InputEdgeCrossings& input_crossings,
+ vector<Graph::Edge>* new_edges,
+ vector<InputEdgeIdSetId>* new_input_edge_ids);
+ void Run();
+
+ private:
+ void AddEdge(Graph::Edge edge, InputEdgeId input_edge_id);
+ void GatherIncidentEdges(
+ const vector<VertexId>& a, int ai,
+ const vector<CrossingInputEdge>& b_input_edges,
+ vector<CrossingGraphEdgeVector>* b_edges) const;
+ int GetCrossedVertexIndex(
+ const vector<VertexId>& a, const CrossingGraphEdgeVector& b,
+ bool left_to_right) const;
+ int GetVertexRank(const CrossingGraphEdge& e) const;
+ bool EdgeChainOnLeft(const vector<VertexId>& a,
+ EdgeId b_first, EdgeId b_last) const;
+
+ const Graph& g_;
+ Graph::VertexInMap in_;
+ Graph::VertexOutMap out_;
+ const vector<int8>& input_dimensions_;
+ const InputEdgeCrossings& input_crossings_;
+ vector<Graph::Edge>* new_edges_;
+ vector<InputEdgeIdSetId>* new_input_edge_ids_;
+
+ // Every graph edge is associated with exactly one input edge in our case,
+ // which means that we can declare g_.input_edge_id_set_ids() as a vector of
+ // InputEdgeIds rather than a vector of InputEdgeIdSetIds. (This also takes
+ // advantage of the fact that IdSetLexicon represents a singleton set as the
+ // value of its single element.)
+ const vector<InputEdgeId>& input_ids_;
+
+ vector<EdgeId> order_; // Graph edges sorted in input edge id order.
+ vector<int> rank_; // The rank of each graph edge within order_.
+};
+
+GraphEdgeClipper::GraphEdgeClipper(
+ const Graph& g, const vector<int8>& input_dimensions,
+ const InputEdgeCrossings& input_crossings,
+ vector<Graph::Edge>* new_edges,
+ vector<InputEdgeIdSetId>* new_input_edge_ids)
+ : g_(g), in_(g), out_(g),
+ input_dimensions_(input_dimensions),
+ input_crossings_(input_crossings),
+ new_edges_(new_edges),
+ new_input_edge_ids_(new_input_edge_ids),
+ input_ids_(g.input_edge_id_set_ids()),
+ order_(GetInputEdgeChainOrder(g_, input_ids_)),
+ rank_(order_.size()) {
+ for (int i = 0; i < order_.size(); ++i) {
+ rank_[order_[i]] = i;
+ }
+}
+
+inline void GraphEdgeClipper::AddEdge(Graph::Edge edge,
+ InputEdgeId input_edge_id) {
+ new_edges_->push_back(edge);
+ new_input_edge_ids_->push_back(input_edge_id);
+}
+
+void GraphEdgeClipper::Run() {
+ // Declare vectors here and reuse them to avoid reallocation.
+ vector<VertexId> a_vertices;
+ vector<int> a_num_crossings;
+ vector<bool> a_isolated;
+ vector<CrossingInputEdge> b_input_edges;
+ vector<CrossingGraphEdgeVector> b_edges;
+
+ bool inside = false;
+ bool invert_b = false;
+ bool reverse_a = false;
+ auto next = input_crossings_.begin();
+ for (int i = 0; i < order_.size(); ++i) {
+ // For each input edge (the "A" input edge), gather all the input edges
+ // that cross it (the "B" input edges).
+ InputEdgeId a_input_id = input_ids_[order_[i]];
+ const Graph::Edge& edge0 = g_.edge(order_[i]);
+ b_input_edges.clear();
+ for (; next != input_crossings_.end(); ++next) {
+ if (next->first != a_input_id) break;
+ if (next->second.input_id() >= 0) {
+ b_input_edges.push_back(next->second);
+ } else if (next->second.input_id() == kSetInside) {
+ inside = next->second.left_to_right();
+ } else if (next->second.input_id() == kSetInvertB) {
+ invert_b = next->second.left_to_right();
+ } else {
+ S2_DCHECK_EQ(next->second.input_id(), kSetReverseA);
+ reverse_a = next->second.left_to_right();
+ }
+ }
+ // Optimization for degenerate edges.
+ // TODO(ericv): If the output layer for this edge dimension specifies
+ // DegenerateEdges::DISCARD, then remove the edge here.
+ if (edge0.first == edge0.second) {
+ inside ^= (b_input_edges.size() & 1);
+ AddEdge(edge0, a_input_id);
+ continue;
+ }
+ // Optimization for the case where there are no crossings.
+ if (b_input_edges.empty()) {
+ // In general the caller only passes edges that are part of the output
+ // (i.e., we could S2_DCHECK(inside) here). The one exception is for
+ // polyline/polygon operations, where the polygon edges are needed to
+ // compute the polyline output but are not emitted themselves.
+ if (inside) {
+ AddEdge(reverse_a ? Graph::reverse(edge0) : edge0, a_input_id);
+ }
+ continue;
+ }
+ // Walk along the chain of snapped edges for input edge A, and at each
+ // vertex collect all the incident edges that belong to one of the
+ // crossing edge chains (the "B" input edges).
+ a_vertices.clear();
+ a_vertices.push_back(edge0.first);
+ b_edges.clear();
+ b_edges.resize(b_input_edges.size());
+ GatherIncidentEdges(a_vertices, 0, b_input_edges, &b_edges);
+ for (; i < order_.size() && input_ids_[order_[i]] == a_input_id; ++i) {
+ a_vertices.push_back(g_.edge(order_[i]).second);
+ GatherIncidentEdges(a_vertices, a_vertices.size() - 1, b_input_edges,
+ &b_edges);
+ }
+ --i;
+ if (s2builder_verbose) {
+ cpp_compat_cout << "input edge " << a_input_id << " (inside=" << inside << "):";
+ for (VertexId id : a_vertices) cpp_compat_cout << " " << id;
+ }
+ // Now for each B edge chain, decide which vertex of the A chain it
+ // crosses, and keep track of the number of signed crossings at each A
+ // vertex. The sign of a crossing depends on whether the other edge
+ // crosses from left to right or right to left.
+ //
+ // This would not be necessary if all calculations were done in exact
+ // arithmetic, because crossings would have strictly alternating signs.
+ // But because we have already snapped the result, some crossing locations
+ // are ambiguous, and GetCrossedVertexIndex() handles this by choosing a
+ // candidate vertex arbitrarily. The end result is that rarely, we may
+ // see two crossings in a row with the same sign. We correct for this by
+ // adding extra output edges that essentially link up the crossings in the
+ // correct (alternating sign) order. Compared to the "correct" behavior,
+ // the only difference is that we have added some extra sibling pairs
+ // (consisting of an edge and its corresponding reverse edge) which do not
+ // affect the result.
+ a_num_crossings.clear();
+ a_num_crossings.resize(a_vertices.size());
+ a_isolated.clear();
+ a_isolated.resize(a_vertices.size());
+ for (int bi = 0; bi < b_input_edges.size(); ++bi) {
+ bool left_to_right = b_input_edges[bi].left_to_right();
+ int a_index = GetCrossedVertexIndex(a_vertices, b_edges[bi],
+ left_to_right);
+ if (a_index >= 0) {
+ if (s2builder_verbose) {
+ cpp_compat_cout << std::endl << " " << "b input edge "
+ << b_input_edges[bi].input_id() << " (l2r=" << left_to_right
+ << ", crossing=" << a_vertices[a_index] << ")";
+ for (const auto& x : b_edges[bi]) {
+ const Graph::Edge& e = g_.edge(x.id);
+ cpp_compat_cout << " (" << e.first << ", " << e.second << ")";
+ }
+ }
+ // Keep track of the number of signed crossings (see above).
+ bool is_line = input_dimensions_[b_input_edges[bi].input_id()] == 1;
+ int sign = is_line ? 0 : (left_to_right == invert_b) ? -1 : 1;
+ a_num_crossings[a_index] += sign;
+
+ // Any polyline or polygon vertex that has at least one crossing but no
+ // adjacent emitted edge may be emitted as an isolated vertex.
+ a_isolated[a_index] = true;
+ } else {
+ // TODO(b/112043775): fix this condition.
+ S2_LOG(DFATAL) << "Failed to get crossed vertex index.";
+ }
+ }
+ if (s2builder_verbose) cpp_compat_cout << std::endl;
+
+ // Finally, we iterate through the A edge chain, keeping track of the
+ // number of signed crossings as we go along. The "multiplicity" is
+ // defined as the cumulative number of signed crossings, and indicates how
+ // many edges should be output (and in which direction) in order to link
+ // up the edge crossings in the correct order. (The multiplicity is
+ // almost always either 0 or 1 except in very rare cases.)
+ int multiplicity = inside + a_num_crossings[0];
+ for (int ai = 1; ai < a_vertices.size(); ++ai) {
+ if (multiplicity != 0) {
+ a_isolated[ai - 1] = a_isolated[ai] = false;
+ }
+ int edge_count = reverse_a ? -multiplicity : multiplicity;
+ // Output any forward edges required.
+ for (int i = 0; i < edge_count; ++i) {
+ AddEdge(Graph::Edge(a_vertices[ai - 1], a_vertices[ai]), a_input_id);
+ }
+ // Output any reverse edges required.
+ for (int i = edge_count; i < 0; ++i) {
+ AddEdge(Graph::Edge(a_vertices[ai], a_vertices[ai - 1]), a_input_id);
+ }
+ multiplicity += a_num_crossings[ai];
+ }
+ // Multiplicities other than 0 or 1 can only occur in the edge interior.
+ S2_DCHECK(multiplicity == 0 || multiplicity == 1);
+ inside = (multiplicity != 0);
+
+ // Output any isolated polyline vertices.
+ // TODO(ericv): Only do this if an output layer wants degenerate edges.
+ if (input_dimensions_[a_input_id] != 0) {
+ for (int ai = 0; ai < a_vertices.size(); ++ai) {
+ if (a_isolated[ai]) {
+ AddEdge(Graph::Edge(a_vertices[ai], a_vertices[ai]), a_input_id);
+ }
+ }
+ }
+ }
+}
+
+// Given the vertices of the snapped edge chain for an input edge A and the
+// set of input edges B that cross input edge A, this method gathers all of
+// the snapped edges of B that are incident to a given snapped vertex of A.
+// The incident edges for each input edge of B are appended to a separate
+// output vector. (A and B can refer to either the input edge or the
+// corresponding snapped edge chain.)
+void GraphEdgeClipper::GatherIncidentEdges(
+ const vector<VertexId>& a, int ai,
+ const vector<CrossingInputEdge>& b_input_edges,
+ vector<CrossingGraphEdgeVector>* b_edges) const {
+ // Examine all of the edges incident to the given vertex of A. If any edge
+ // comes from a B input edge, append it to the appropriate vector.
+ S2_DCHECK_EQ(b_input_edges.size(), b_edges->size());
+ for (EdgeId e : in_.edge_ids(a[ai])) {
+ InputEdgeId id = input_ids_[e];
+ auto it = lower_bound(b_input_edges.begin(), b_input_edges.end(), id);
+ if (it != b_input_edges.end() && it->input_id() == id) {
+ auto& edges = (*b_edges)[it - b_input_edges.begin()];
+ edges.push_back(CrossingGraphEdge(e, ai, false, g_.edge(e).first));
+ }
+ }
+ for (EdgeId e : out_.edge_ids(a[ai])) {
+ InputEdgeId id = input_ids_[e];
+ auto it = lower_bound(b_input_edges.begin(), b_input_edges.end(), id);
+ if (it != b_input_edges.end() && it->input_id() == id) {
+ auto& edges = (*b_edges)[it - b_input_edges.begin()];
+ edges.push_back(CrossingGraphEdge(e, ai, true, g_.edge(e).second));
+ }
+ }
+}
+
+// Returns the "vertex rank" of the shared vertex associated with the given
+// CrossingGraphEdge. Recall that graph edges are sorted in input edge order,
+// and that the rank of an edge is its position in this order (rank_[e]).
+// VertexRank(e) is defined such that VertexRank(e.src) == rank_[e] and
+// VertexRank(e.dst) == rank_[e] + 1. Note that the concept of "vertex rank"
+// is only defined within a single edge chain (since different edge chains can
+// have overlapping vertex ranks).
+int GraphEdgeClipper::GetVertexRank(const CrossingGraphEdge& e) const {
+ return rank_[e.id] + !e.outgoing;
+}
+
+// Given an edge chain A that is crossed by another edge chain B (where
+// "left_to_right" indicates whether B crosses A from left to right), this
+// method decides which vertex of A the crossing takes place at. The
+// parameters are the vertices of the A chain ("a") and the set of edges in
+// the B chain ("b") that are incident to vertices of A. The B chain edges
+// are sorted in increasing order of (a_index, outgoing) tuple.
+int GraphEdgeClipper::GetCrossedVertexIndex(
+ const vector<VertexId>& a, const CrossingGraphEdgeVector& b,
+ bool left_to_right) const {
+ S2_DCHECK(!a.empty());
+ S2_DCHECK(!b.empty());
+
+ // The reason this calculation is tricky is that after snapping, the A and B
+ // chains may meet and separate several times. For example, if B crosses A
+ // from left to right, then B may touch A, make an excursion to the left of
+ // A, come back to A, then make an excursion to the right of A and come back
+ // to A again, like this:
+ //
+ // *--B--*-\ /-*-\
+ // B-\ /-B B-\ 6 7 8 9
+ // *--A--*--A--*-A,B-*--A--*--A--*-A,B-*--A--*--A--*-A,B-*
+ // 0 1 2 3 4 5 \-B B-/
+ // \-*-/
+ //
+ // (where "*" is a vertex, and "A" and "B" are edge labels). Note that B
+ // may also follow A for one or more edges whenever they touch (e.g. between
+ // vertices 2 and 3). In this case the only vertices of A where the
+ // crossing could take place are 5 and 6, i.e. after all excursions of B to
+ // the left of A, and before all excursions of B to the right of A.
+ //
+ // Other factors to consider are that the portion of B before and/or after
+ // the crossing may be degenerate, and some or all of the B edges may be
+ // reversed relative to the A edges.
+
+ // First, check whether edge A is degenerate.
+ int n = a.size();
+ if (n == 1) return 0;
+
+ // If edge chain B is incident to only one vertex of A, we're done.
+ if (b[0].a_index == b.back().a_index) return b[0].a_index;
+
+ // Determine whether the B chain visits the first and last vertices that it
+ // shares with the A chain in the same order or the reverse order. This is
+ // only needed to implement one special case (see below).
+ bool b_reversed = GetVertexRank(b[0]) > GetVertexRank(b.back());
+
+ // Examine each incident B edge and use it to narrow the range of positions
+ // where the crossing could occur in the B chain. Vertex positions are
+ // represented as a range [lo, hi] of vertex ranks in the B chain (see
+ // GetVertexRank).
+ //
+ // Note that if an edge of B is incident to the first or last vertex of A,
+ // we can't test which side of the A chain it is on. (An s2pred::Sign test
+ // doesn't work; e.g. if the B edge is XY and the first edge of A is YZ,
+ // then snapping can change the sign of XYZ while maintaining topological
+ // guarantees.) There can be up to 4 such edges (one incoming and one
+ // outgoing edge at each endpoint of A). Two of these edges logically
+ // extend past the end of the A chain and place no restrictions on the
+ // crossing vertex. The other two edges define the ends of the subchain
+ // where B shares vertices with A. We save these edges in order to handle a
+ // special case (see below).
+ int lo = -1, hi = order_.size(); // Vertex ranks of acceptable crossings
+ EdgeId b_first = -1, b_last = -1; // "b" subchain connecting "a" endpoints
+ for (const auto& e : b) {
+ int ai = e.a_index;
+ if (ai == 0) {
+ if (e.outgoing != b_reversed && e.dst != a[1]) b_first = e.id;
+ } else if (ai == n - 1) {
+ if (e.outgoing == b_reversed && e.dst != a[n - 2]) b_last = e.id;
+ } else {
+ // This B edge is incident to an interior vertex of the A chain. First
+ // check whether this edge is identical (or reversed) to an edge in the
+ // A chain, in which case it does not create any restrictions.
+ if (e.dst == a[ai - 1] || e.dst == a[ai + 1]) continue;
+
+ // Otherwise we can test which side of the A chain the edge lies on.
+ bool on_left = s2pred::OrderedCCW(g_.vertex(a[ai + 1]), g_.vertex(e.dst),
+ g_.vertex(a[ai - 1]), g_.vertex(a[ai]));
+
+ // Every B edge that is incident to an interior vertex of the A chain
+ // places some restriction on where the crossing vertex could be.
+ if (left_to_right == on_left) {
+ // This is a pre-crossing edge, so the crossing cannot be before the
+ // destination vertex of this edge. (For example, the input B edge
+ // crosses the input A edge from left to right and this edge of the B
+ // chain is to the left of the A chain.)
+ lo = max(lo, rank_[e.id] + 1);
+ } else {
+ // This is a post-crossing edge, so the crossing cannot be after the
+ // source vertex of this edge.
+ hi = min(hi, rank_[e.id]);
+ }
+ }
+ }
+ // There is one special case. If a subchain of B connects the first and
+ // last vertices of A, then together with the edges of A this forms a loop
+ // whose orientation can be tested to determine whether B is on the left or
+ // right side of A. This is only possible (and only necessary) if the B
+ // subchain does not include any interior vertices of A, since otherwise the
+ // B chain might cross from one side of A to the other.
+ //
+ // Note that it would be possible to avoid this test in some situations by
+ // checking whether either endpoint of the A chain has two incident B edges,
+ // in which case we could check which side of the B chain the A edge is on
+ // and use this to limit the possible crossing locations.
+ if (b_first >= 0 && b_last >= 0) {
+ // The B subchain connects the first and last vertices of A. Test whether
+ // the chain includes any interior vertices of A. We do this indirectly
+ // by testing whether any edge of B has restricted the range of allowable
+ // crossing vertices (since any interior edge of the B subchain incident
+ // to any interior edge of A is guaranteed to do so).
+ int min_rank = order_.size(), max_rank = -1;
+ for (const auto& e : b) {
+ min_rank = min(min_rank, GetVertexRank(e));
+ max_rank = max(max_rank, GetVertexRank(e));
+ }
+ if (lo <= min_rank && hi >= max_rank) {
+ // The B subchain is not incident to any interior vertex of A.
+ // Swap the edges if necessary so that they are in B chain order.
+ if (b_reversed) swap(b_first, b_last);
+ bool on_left = EdgeChainOnLeft(a, b_first, b_last);
+ if (left_to_right == on_left) {
+ lo = max(lo, rank_[b_last] + 1);
+ } else {
+ hi = min(hi, rank_[b_first]);
+ }
+ }
+ }
+
+ // Otherwise we choose the smallest shared VertexId in the acceptable range,
+ // in order to ensure that both chains choose the same crossing vertex.
+ int best = -1;
+ S2_DCHECK_LE(lo, hi);
+ for (const auto& e : b) {
+ int ai = e.a_index;
+ int vrank = GetVertexRank(e);
+ if (vrank >= lo && vrank <= hi && (best < 0 || a[ai] < a[best])) {
+ best = ai;
+ }
+ }
+ return best;
+}
+
+// Given edge chains A and B that form a loop (after possibly reversing the
+// direction of chain B), returns true if chain B is to the left of chain A.
+// Chain A is given as a sequence of vertices, while chain B is specified as
+// the first and last edges of the chain.
+bool GraphEdgeClipper::EdgeChainOnLeft(
+ const vector<VertexId>& a, EdgeId b_first, EdgeId b_last) const {
+ // Gather all the interior vertices of the B subchain.
+ vector<VertexId> loop;
+ for (int i = rank_[b_first]; i < rank_[b_last]; ++i) {
+ loop.push_back(g_.edge(order_[i]).second);
+ }
+ // Possibly reverse the chain so that it forms a loop when "a" is appended.
+ if (g_.edge(b_last).second != a[0]) std::reverse(loop.begin(), loop.end());
+ loop.insert(loop.end(), a.begin(), a.end());
+ // Duplicate the first two vertices to simplify vertex indexing.
+ for (int j = 0; j < 2; j++) {
+ loop.insert(loop.end(), *(loop.begin() + j));
+ }
+ // Now B is to the left of A if and only if the loop is counterclockwise.
+ double sum = 0;
+ for (int i = 2; i < loop.size(); ++i) {
+ sum += S2::TurnAngle(g_.vertex(loop[i - 2]), g_.vertex(loop[i - 1]),
+ g_.vertex(loop[i]));
+ }
+ return sum > 0;
+}
+
+// Given a set of clipping instructions encoded as a set of intersections
+// between input edges, EdgeClippingLayer determines which graph edges
+// correspond to clipped portions of input edges and removes them. It
+// assembles the remaining edges into a new S2Builder::Graph and passes the
+// result to the given output layer for assembly.
+class EdgeClippingLayer : public S2Builder::Layer {
+ public:
+ EdgeClippingLayer(const vector<unique_ptr<S2Builder::Layer>>* layers,
+ const vector<int8>* input_dimensions,
+ const InputEdgeCrossings* input_crossings)
+ : layers_(*layers),
+ input_dimensions_(*input_dimensions),
+ input_crossings_(*input_crossings) {
+ }
+
+ // Layer interface:
+ GraphOptions graph_options() const override;
+ void Build(const Graph& g, S2Error* error) override;
+
+ private:
+ const vector<unique_ptr<S2Builder::Layer>>& layers_;
+ const vector<int8>& input_dimensions_;
+ const InputEdgeCrossings& input_crossings_;
+};
+
+GraphOptions EdgeClippingLayer::graph_options() const {
+ // We keep all edges, including degenerate ones, so that we can figure out
+ // the correspondence between input edge crossings and output edge
+ // crossings.
+ return GraphOptions(EdgeType::DIRECTED, DegenerateEdges::KEEP,
+ DuplicateEdges::KEEP, SiblingPairs::KEEP);
+}
+
+// Helper function (in anonymous namespace) to create an S2Builder::Graph from
+// a vector of edges.
+Graph MakeGraph(
+ const Graph& g, GraphOptions* options, vector<Graph::Edge>* new_edges,
+ vector<InputEdgeIdSetId>* new_input_edge_ids,
+ IdSetLexicon* new_input_edge_id_set_lexicon, S2Error* error) {
+ if (options->edge_type() == EdgeType::UNDIRECTED) {
+ // Create a reversed edge for every edge.
+ int n = new_edges->size();
+ new_edges->reserve(2 * n);
+ new_input_edge_ids->reserve(2 * n);
+ for (int i = 0; i < n; ++i) {
+ new_edges->push_back(Graph::reverse((*new_edges)[i]));
+ new_input_edge_ids->push_back(IdSetLexicon::EmptySetId());
+ }
+ }
+ Graph::ProcessEdges(options, new_edges, new_input_edge_ids,
+ new_input_edge_id_set_lexicon, error);
+ return Graph(*options, &g.vertices(), new_edges, new_input_edge_ids,
+ new_input_edge_id_set_lexicon, &g.label_set_ids(),
+ &g.label_set_lexicon(), g.is_full_polygon_predicate());
+}
+
+void EdgeClippingLayer::Build(const Graph& g, S2Error* error) {
+ // The bulk of the work is handled by GraphEdgeClipper.
+ vector<Graph::Edge> new_edges;
+ vector<InputEdgeIdSetId> new_input_edge_ids;
+ // Destroy the GraphEdgeClipper immediately to save memory.
+ GraphEdgeClipper(g, input_dimensions_, input_crossings_,
+ &new_edges, &new_input_edge_ids).Run();
+ if (s2builder_verbose) {
+ cpp_compat_cout << "Edges after clipping: " << std::endl;
+ for (int i = 0; i < new_edges.size(); ++i) {
+ cpp_compat_cout << " " << new_input_edge_ids[i] << " (" << new_edges[i].first
+ << ", " << new_edges[i].second << ")" << std::endl;
+ }
+ }
+ // Construct one or more graphs from the clipped edges and pass them to the
+ // given output layer(s).
+ IdSetLexicon new_input_edge_id_set_lexicon;
+ if (layers_.size() == 1) {
+ GraphOptions options = layers_[0]->graph_options();
+ Graph new_graph = MakeGraph(g, &options, &new_edges, &new_input_edge_ids,
+ &new_input_edge_id_set_lexicon, error);
+ layers_[0]->Build(new_graph, error);
+ } else {
+ // The Graph objects must be valid until the last Build() call completes,
+ // so we store all of the graph data in arrays with 3 elements.
+ S2_DCHECK_EQ(3, layers_.size());
+ vector<Graph::Edge> layer_edges[3];
+ vector<InputEdgeIdSetId> layer_input_edge_ids[3];
+ S2Builder::GraphOptions layer_options[3];
+ vector<S2Builder::Graph> layer_graphs; // No default constructor.
+ layer_graphs.reserve(3);
+ // Separate the edges according to their dimension.
+ for (int i = 0; i < new_edges.size(); ++i) {
+ int d = input_dimensions_[new_input_edge_ids[i]];
+ layer_edges[d].push_back(new_edges[i]);
+ layer_input_edge_ids[d].push_back(new_input_edge_ids[i]);
+ }
+ // Clear variables to save space.
+ vector<Graph::Edge>().swap(new_edges);
+ vector<InputEdgeIdSetId>().swap(new_input_edge_ids);
+ for (int d = 0; d < 3; ++d) {
+ layer_options[d] = layers_[d]->graph_options();
+ layer_graphs.push_back(MakeGraph(
+ g, &layer_options[d], &layer_edges[d], &layer_input_edge_ids[d],
+ &new_input_edge_id_set_lexicon, error));
+ layers_[d]->Build(layer_graphs[d], error);
+ }
+ }
+}
+
+} // namespace
+
+class S2BooleanOperation::Impl {
+ public:
+ explicit Impl(S2BooleanOperation* op)
+ : op_(op), index_crossings_first_region_id_(-1) {
+ }
+ bool Build(S2Error* error);
+
+ private:
+ class CrossingIterator;
+ class CrossingProcessor;
+ using ShapeEdge = s2shapeutil::ShapeEdge;
+ using ShapeEdgeId = s2shapeutil::ShapeEdgeId;
+
+ // An IndexCrossing represents a pair of intersecting S2ShapeIndex edges
+ // ("a_edge" and "b_edge"). We store all such intersections because the
+ // algorithm needs them twice, once when processing the boundary of region A
+ // and once when processing the boundary of region B.
+ struct IndexCrossing {
+ ShapeEdgeId a, b;
+
+ // True if S2::CrossingSign(a_edge, b_edge) > 0.
+ uint32 is_interior_crossing : 1;
+
+ // True if "a_edge" crosses "b_edge" from left to right. Undefined if
+ // is_interior_crossing is false.
+ uint32 left_to_right: 1;
+
+ // Equal to S2::VertexCrossing(a_edge, b_edge). Undefined if "a_edge" and
+ // "b_edge" do not share exactly one vertex or either edge is degenerate.
+ uint32 is_vertex_crossing : 1;
+
+ // All flags are "false" by default.
+ IndexCrossing(ShapeEdgeId _a, ShapeEdgeId _b)
+ : a(_a), b(_b), is_interior_crossing(false), left_to_right(false),
+ is_vertex_crossing(false) {
+ }
+
+ friend bool operator==(const IndexCrossing& x, const IndexCrossing& y) {
+ return x.a == y.a && x.b == y.b;
+ }
+ friend bool operator<(const IndexCrossing& x, const IndexCrossing& y) {
+ // The compiler (2017) doesn't optimize the following as well:
+ // return x.a < y.a || (x.a == y.a && x.b < y.b);
+ if (x.a.shape_id < y.a.shape_id) return true;
+ if (y.a.shape_id < x.a.shape_id) return false;
+ if (x.a.edge_id < y.a.edge_id) return true;
+ if (y.a.edge_id < x.a.edge_id) return false;
+ if (x.b.shape_id < y.b.shape_id) return true;
+ if (y.b.shape_id < x.b.shape_id) return false;
+ return x.b.edge_id < y.b.edge_id;
+ }
+ };
+ using IndexCrossings = vector<IndexCrossing>;
+
+ bool is_boolean_output() const { return op_->result_empty_ != nullptr; }
+
+ // All of the methods below support "early exit" in the case of boolean
+ // results by returning "false" as soon as the result is known to be
+ // non-empty.
+ bool AddBoundary(int a_region_id, bool invert_a, bool invert_b,
+ bool invert_result,
+ const vector<ShapeEdgeId>& a_chain_starts,
+ CrossingProcessor* cp);
+ bool GetChainStarts(int a_region_id, bool invert_a, bool invert_b,
+ bool invert_result, CrossingProcessor* cp,
+ vector<ShapeEdgeId>* chain_starts);
+ bool ProcessIncidentEdges(const ShapeEdge& a,
+ S2ContainsPointQuery<S2ShapeIndex>* query,
+ CrossingProcessor* cp);
+ static bool HasInterior(const S2ShapeIndex& index);
+ static bool AddIndexCrossing(const ShapeEdge& a, const ShapeEdge& b,
+ bool is_interior, IndexCrossings* crossings);
+ bool GetIndexCrossings(int region_id);
+ bool AddBoundaryPair(bool invert_a, bool invert_b, bool invert_result,
+ CrossingProcessor* cp);
+ bool AreRegionsIdentical() const;
+ bool BuildOpType(OpType op_type);
+ bool IsFullPolygonResult(const S2Builder::Graph& g, S2Error* error) const;
+ bool IsFullPolygonUnion(const S2ShapeIndex& a,
+ const S2ShapeIndex& b) const;
+ bool IsFullPolygonIntersection(const S2ShapeIndex& a,
+ const S2ShapeIndex& b) const;
+ bool IsFullPolygonDifference(const S2ShapeIndex& a,
+ const S2ShapeIndex& b) const;
+ bool IsFullPolygonSymmetricDifference(const S2ShapeIndex& a,
+ const S2ShapeIndex& b) const;
+
+ // A bit mask representing all six faces of the S2 cube.
+ static constexpr uint8 kAllFacesMask = 0x3f;
+
+ S2BooleanOperation* op_;
+
+ // The S2Builder used to construct the output.
+ unique_ptr<S2Builder> builder_;
+
+ // A vector specifying the dimension of each edge added to S2Builder.
+ vector<int8> input_dimensions_;
+
+ // The set of all input edge crossings, which is used by EdgeClippingLayer
+ // to construct the clipped output polygon.
+ InputEdgeCrossings input_crossings_;
+
+ // kSentinel is a sentinel value used to mark the end of vectors.
+ static const ShapeEdgeId kSentinel;
+
+ // A vector containing all pairs of crossing edges from the two input
+ // regions (including edge pairs that share a common vertex). The first
+ // element of each pair is an edge from "index_crossings_first_region_id_",
+ // while the second element of each pair is an edge from the other region.
+ IndexCrossings index_crossings_;
+
+ // Indicates that the first element of each crossing edge pair in
+ // "index_crossings_" corresponds to an edge from the given region.
+ // This field is negative if index_crossings_ has not been computed yet.
+ int index_crossings_first_region_id_;
+
+ // Temporary storage used in GetChainStarts(), declared here to avoid
+ // repeatedly allocating memory.
+ IndexCrossings tmp_crossings_;
+};
+
+const s2shapeutil::ShapeEdgeId S2BooleanOperation::Impl::kSentinel(
+ std::numeric_limits<int32>::max(), 0);
+
+// A helper class for iterating through the edges from region B that cross a
+// particular edge from region A. It caches information from the current
+// shape, chain, and edge so that it doesn't need to be looked up repeatedly.
+// Typical usage:
+//
+// void SomeFunction(ShapeEdgeId a_id, CrossingIterator *it) {
+// // Iterate through the edges that cross edge "a_id".
+// for (; !it->Done(a_id); it->Next()) {
+// ... use it->b_shape(), it->b_edge(), etc ...
+// }
+class S2BooleanOperation::Impl::CrossingIterator {
+ public:
+ // Creates an iterator over crossing edge pairs (a, b) where "b" is an edge
+ // from "b_index". "crossings_complete" indicates that "crossings" contains
+ // all edge crossings between the two regions (rather than a subset).
+ CrossingIterator(const S2ShapeIndex* b_index,
+ const IndexCrossings* crossings, bool crossings_complete)
+ : b_index_(*b_index), it_(crossings->begin()), b_shape_id_(-1),
+ crossings_complete_(crossings_complete) {
+ Update();
+ }
+ void Next() {
+ ++it_;
+ Update();
+ }
+ bool Done(ShapeEdgeId id) const { return a_id() != id; }
+
+ // True if all edge crossings are available (see above).
+ bool crossings_complete() const { return crossings_complete_; }
+
+ // True if this crossing occurs at a point interior to both edges.
+ bool is_interior_crossing() const { return it_->is_interior_crossing; }
+
+ // Equal to S2::VertexCrossing(a_edge, b_edge), provided that a_edge and
+ // b_edge have exactly one vertex in common and neither edge is degenerate.
+ bool is_vertex_crossing() const { return it_->is_vertex_crossing; }
+
+ // True if a_edge crosses b_edge from left to right (for interior crossings).
+ bool left_to_right() const { return it_->left_to_right; }
+
+ ShapeEdgeId a_id() const { return it_->a; }
+ ShapeEdgeId b_id() const { return it_->b; }
+ const S2ShapeIndex& b_index() const { return b_index_; }
+ const S2Shape& b_shape() const { return *b_shape_; }
+ int b_dimension() const { return b_dimension_; }
+ int b_shape_id() const { return b_shape_id_; }
+ int b_edge_id() const { return b_id().edge_id; }
+
+ S2Shape::Edge b_edge() const {
+ return b_shape_->edge(b_edge_id()); // Opportunity to cache this.
+ }
+
+ // Information about the chain to which an edge belongs.
+ struct ChainInfo {
+ int chain_id; // chain id
+ int start; // starting edge id
+ int limit; // limit edge id
+ };
+ // Returns a description of the chain to which the current B edge belongs.
+ const ChainInfo& b_chain_info() const {
+ if (b_info_.chain_id < 0) {
+ b_info_.chain_id = b_shape().chain_position(b_edge_id()).chain_id;
+ auto chain = b_shape().chain(b_info_.chain_id);
+ b_info_.start = chain.start;
+ b_info_.limit = chain.start + chain.length;
+ }
+ return b_info_;
+ }
+
+ private:
+ // Updates information about the B shape whenever it changes.
+ void Update() {
+ if (a_id() != kSentinel && b_id().shape_id != b_shape_id_) {
+ b_shape_id_ = b_id().shape_id;
+ b_shape_ = b_index_.shape(b_shape_id_);
+ b_dimension_ = b_shape_->dimension();
+ b_info_.chain_id = -1; // Computed on demand.
+ }
+ }
+
+ const S2ShapeIndex& b_index_;
+ IndexCrossings::const_iterator it_;
+ const S2Shape* b_shape_;
+ int b_shape_id_;
+ int b_dimension_;
+ mutable ChainInfo b_info_; // Computed on demand.
+ bool crossings_complete_;
+};
+
+// CrossingProcessor is a helper class that processes all the edges from one
+// region that cross a specific edge of the other region. It outputs the
+// appropriate edges to an S2Builder, and outputs other information required
+// by GraphEdgeClipper to the given vectors.
+class S2BooleanOperation::Impl::CrossingProcessor {
+ public:
+ // Prepares to build output for the given polygon and polyline boundary
+ // models. Edges are emitted to "builder", while other auxiliary data is
+ // appended to the given vectors.
+ //
+ // If a predicate is being evaluated (i.e., we do not need to construct the
+ // actual result), then "builder" and the various output vectors should all
+ // be nullptr.
+ CrossingProcessor(const PolygonModel& polygon_model,
+ const PolylineModel& polyline_model,
+ bool polyline_loops_have_boundaries,
+ S2Builder* builder,
+ vector<int8>* input_dimensions,
+ InputEdgeCrossings *input_crossings)
+ : polygon_model_(polygon_model), polyline_model_(polyline_model),
+ polyline_loops_have_boundaries_(polyline_loops_have_boundaries),
+ builder_(builder), input_dimensions_(input_dimensions),
+ input_crossings_(input_crossings), prev_inside_(false) {
+ }
+
+ // Starts processing edges from the given region. "invert_a", "invert_b",
+ // and "invert_result" indicate whether region A, region B, and/or the
+ // result should be inverted, which allows operations such as union and
+ // difference to be implemented. For example, union is ~(~A & ~B).
+ //
+ // This method should be called in pairs, once to process the edges from
+ // region A and once to process the edges from region B.
+ void StartBoundary(int a_region_id, bool invert_a, bool invert_b,
+ bool invert_result);
+
+ // Starts processing edges from the given shape.
+ void StartShape(const S2Shape* a_shape);
+
+ // Starts processing edges from the given chain.
+ void StartChain(int chain_id, S2Shape::Chain chain, bool inside);
+
+ // Processes the given edge "a_id". "it" should be positioned to the set of
+ // edges from the other region that cross "a_id" (if any).
+ //
+ // Supports "early exit" in the case of boolean results by returning false
+ // as soon as the result is known to be non-empty.
+ bool ProcessEdge(ShapeEdgeId a_id, CrossingIterator* it);
+
+ // This method should be called after each pair of calls to StartBoundary.
+ // (The only operation that processes more than one pair of boundaries is
+ // SYMMETRIC_DIFFERENCE, which computes the union of A-B and B-A.)
+ //
+ // Resets the state of the CrossingProcessor.
+ void DoneBoundaryPair();
+
+ // Indicates whether the point being processed along the current edge chain
+ // is in the polygonal interior of the opposite region, using semi-open
+ // boundaries. If "invert_b_" is true then this field is inverted.
+ //
+ // This value along with the set of incident edges can be used to compute
+ // whether the opposite region contains this point under any of the
+ // supported boundary models (PolylineModel::CLOSED, etc).
+ bool inside() const { return inside_; }
+
+ private:
+ // SourceEdgeCrossing represents an input edge that crosses some other
+ // edge; it crosses the edge from left to right iff the second parameter
+ // is "true".
+ using SourceEdgeCrossing = pair<SourceId, bool>;
+ struct PointCrossingResult;
+ struct EdgeCrossingResult;
+
+ InputEdgeId input_edge_id() const { return input_dimensions_->size(); }
+
+ // Returns true if the edges on either side of the first vertex of the
+ // current edge have not been emitted.
+ //
+ // REQUIRES: This method is called just after updating "inside_" for "v0".
+ bool is_v0_isolated(ShapeEdgeId a_id) const {
+ return !inside_ && v0_emitted_max_edge_id_ < a_id.edge_id;
+ }
+
+ // Returns true if "a_id" is the last edge of the current chain, and the
+ // edges on either side of the last vertex have not been emitted (including
+ // the possibility that the chain forms a loop).
+ bool is_chain_last_vertex_isolated(ShapeEdgeId a_id) const {
+ return (a_id.edge_id == chain_limit_ - 1 && !chain_v0_emitted_ &&
+ v0_emitted_max_edge_id_ <= a_id.edge_id);
+ }
+
+ // Returns true if the given polyline edge contains "v0", taking into
+ // account the specified PolylineModel.
+ bool polyline_contains_v0(int edge_id, int chain_start) const {
+ return (polyline_model_ != PolylineModel::OPEN || edge_id > chain_start);
+ }
+
+ void AddCrossing(const SourceEdgeCrossing& crossing) {
+ source_edge_crossings_.push_back(make_pair(input_edge_id(), crossing));
+ }
+
+ void SetClippingState(InputEdgeId parameter, bool state) {
+ AddCrossing(SourceEdgeCrossing(SourceId(parameter), state));
+ }
+
+ // Supports "early exit" in the case of boolean results by returning false
+ // as soon as the result is known to be non-empty.
+ bool AddEdge(ShapeEdgeId a_id, const S2Shape::Edge& a,
+ int dimension, int interior_crossings) {
+ if (builder_ == nullptr) return false; // Boolean output.
+ if (interior_crossings > 0) {
+ // Build a map that translates temporary edge ids (SourceId) to
+ // the representation used by EdgeClippingLayer (InputEdgeId).
+ SourceId src_id(a_region_id_, a_id.shape_id, a_id.edge_id);
+ source_id_map_[src_id] = input_edge_id();
+ }
+ // Set the GraphEdgeClipper's "inside" state to match ours.
+ if (inside_ != prev_inside_) SetClippingState(kSetInside, inside_);
+ input_dimensions_->push_back(dimension);
+ builder_->AddEdge(a.v0, a.v1);
+ inside_ ^= (interior_crossings & 1);
+ prev_inside_ = inside_;
+ return true;
+ }
+
+ // Supports "early exit" in the case of boolean results by returning false
+ // as soon as the result is known to be non-empty.
+ bool AddPointEdge(const S2Point& p, int dimension) {
+ if (builder_ == nullptr) return false; // Boolean output.
+ if (!prev_inside_) SetClippingState(kSetInside, true);
+ input_dimensions_->push_back(dimension);
+ builder_->AddEdge(p, p);
+ prev_inside_ = true;
+ return true;
+ }
+
+ bool ProcessEdge0(ShapeEdgeId a_id, const S2Shape::Edge& a,
+ CrossingIterator* it);
+ bool ProcessEdge1(ShapeEdgeId a_id, const S2Shape::Edge& a,
+ CrossingIterator* it);
+ bool ProcessEdge2(ShapeEdgeId a_id, const S2Shape::Edge& a,
+ CrossingIterator* it);
+
+ void SkipCrossings(ShapeEdgeId a_id, CrossingIterator* it);
+ PointCrossingResult ProcessPointCrossings(
+ ShapeEdgeId a_id, const S2Point& a0, CrossingIterator* it) const;
+ EdgeCrossingResult ProcessEdgeCrossings(
+ ShapeEdgeId a_id, const S2Shape::Edge& a, CrossingIterator* it);
+
+ bool IsPolylineVertexInside(bool matches_polyline,
+ bool matches_polygon) const;
+ bool IsPolylineEdgeInside(const EdgeCrossingResult& r) const;
+ bool PolylineEdgeContainsVertex(const S2Point& v,
+ const CrossingIterator& it) const;
+
+ // Constructor parameters:
+
+ PolygonModel polygon_model_;
+ PolylineModel polyline_model_;
+ bool polyline_loops_have_boundaries_;
+
+ // The output of the CrossingProcessor consists of a subset of the input
+ // edges that are emitted to "builder_", and some auxiliary information
+ // that allows GraphEdgeClipper to determine which segments of those input
+ // edges belong to the output. The auxiliary information consists of the
+ // dimension of each input edge, and set of input edges from the other
+ // region that cross each input input edge.
+ S2Builder* builder_;
+ vector<int8>* input_dimensions_;
+ InputEdgeCrossings* input_crossings_;
+
+ // Fields set by StartBoundary:
+
+ int a_region_id_, b_region_id_;
+ bool invert_a_, invert_b_, invert_result_;
+ bool is_union_; // True if this is a UNION operation.
+
+ // Fields set by StartShape:
+
+ const S2Shape* a_shape_;
+ int a_dimension_;
+
+ // Fields set by StartChain:
+
+ int chain_id_;
+ int chain_start_;
+ int chain_limit_;
+
+ // Fields updated by ProcessEdge:
+
+ // A temporary representation of input_crossings_ that is used internally
+ // until all necessary edges from *both* polygons have been emitted to the
+ // S2Builder. This field is then converted by DoneBoundaryPair() into
+ // the InputEdgeCrossings format expected by GraphEdgeClipper.
+ //
+ // The reason that we can't construct input_crossings_ directly is that it
+ // uses InputEdgeIds to identify the edges from both polygons, and when we
+ // are processing edges from the first polygon, InputEdgeIds have not yet
+ // been assigned to the second polygon. So instead this field identifies
+ // edges from the first polygon using an InputEdgeId, and edges from the
+ // second polygon using a (region_id, shape_id, edge_id) tuple (i.e., a
+ // SourceId).
+ //
+ // All crossings are represented twice, once to indicate that an edge from
+ // polygon 0 is crossed by an edge from polygon 1, and once to indicate that
+ // an edge from polygon 1 is crossed by an edge from polygon 0.
+ using SourceEdgeCrossings = vector<pair<InputEdgeId, SourceEdgeCrossing>>;
+ SourceEdgeCrossings source_edge_crossings_;
+
+ // A map that translates from SourceId (the (region_id, shape_id,
+ // edge_id) triple that identifies an S2ShapeIndex edge) to InputEdgeId (the
+ // sequentially increasing numbers assigned to input edges by S2Builder).
+ using SourceIdMap = gtl::btree_map<SourceId, InputEdgeId>;
+ SourceIdMap source_id_map_;
+
+ // Indicates whether the point being processed along the current edge chain
+ // is in the polygonal interior of the opposite region, using semi-open
+ // boundaries. If "invert_b_" is true then this field is inverted.
+ //
+ // Equal to: b_index_.Contains(current point) ^ invert_b_
+ bool inside_;
+
+ // The value of that "inside_" would have just before the end of the
+ // previous edge added to S2Builder. This value is used to determine
+ // whether the GraphEdgeClipper state needs to be updated when jumping from
+ // one edge chain to another.
+ bool prev_inside_;
+
+ // The maximum edge id of any edge in the current chain whose v0 vertex has
+ // already been emitted. This is used to determine when an isolated vertex
+ // needs to be emitted, e.g. when two closed polygons share only a vertex.
+ int v0_emitted_max_edge_id_;
+
+ // True if the first vertex of the current chain has been emitted. This is
+ // used when processing loops in order to determine whether the first/last
+ // vertex of the loop should be emitted as an isolated vertex.
+ bool chain_v0_emitted_;
+};
+
+// See documentation above.
+void S2BooleanOperation::Impl::CrossingProcessor::StartBoundary(
+ int a_region_id, bool invert_a, bool invert_b, bool invert_result) {
+ a_region_id_ = a_region_id;
+ b_region_id_ = 1 - a_region_id;
+ invert_a_ = invert_a;
+ invert_b_ = invert_b;
+ invert_result_ = invert_result;
+ is_union_ = invert_b && invert_result;
+
+ // Specify to GraphEdgeClipper how these edges should be clipped.
+ SetClippingState(kSetReverseA, invert_a != invert_result);
+ SetClippingState(kSetInvertB, invert_b);
+}
+
+// See documentation above.
+inline void S2BooleanOperation::Impl::CrossingProcessor::StartShape(
+ const S2Shape* a_shape) {
+ a_shape_ = a_shape;
+ a_dimension_ = a_shape->dimension();
+}
+
+// See documentation above.
+inline void S2BooleanOperation::Impl::CrossingProcessor::StartChain(
+ int chain_id, S2Shape::Chain chain, bool inside) {
+ chain_id_ = chain_id;
+ chain_start_ = chain.start;
+ chain_limit_ = chain.start + chain.length;
+ inside_ = inside;
+ v0_emitted_max_edge_id_ = chain.start - 1; // No edges emitted yet.
+ chain_v0_emitted_ = false;
+}
+
+// See documentation above.
+bool S2BooleanOperation::Impl::CrossingProcessor::ProcessEdge(
+ ShapeEdgeId a_id, CrossingIterator* it) {
+ // chain_edge() is faster than edge() when there are multiple chains.
+ auto a = a_shape_->chain_edge(chain_id_, a_id.edge_id - chain_start_);
+ if (a_dimension_ == 0) {
+ return ProcessEdge0(a_id, a, it);
+ } else if (a_dimension_ == 1) {
+ return ProcessEdge1(a_id, a, it);
+ } else {
+ S2_DCHECK_EQ(2, a_dimension_);
+ return ProcessEdge2(a_id, a, it);
+ }
+}
+
+// PointCrossingResult describes the relationship between a point from region A
+// and a set of crossing edges from region B. For example, "matches_polygon"
+// indicates whether a polygon vertex from region B matches the given point.
+struct S2BooleanOperation::Impl::CrossingProcessor::PointCrossingResult {
+ PointCrossingResult()
+ : matches_point(false), matches_polyline(false), matches_polygon(false) {
+ }
+ // Note that "matches_polyline" is true only if the point matches a polyline
+ // vertex of B *and* the polyline contains that vertex, whereas
+ // "matches_polygon" is true if the point matches any polygon vertex.
+ bool matches_point; // Matches point.
+ bool matches_polyline; // Matches contained polyline vertex.
+ bool matches_polygon; // Matches polygon vertex.
+};
+
+// Processes an edge of dimension 0 (i.e., a point) from region A.
+//
+// Supports "early exit" in the case of boolean results by returning false
+// as soon as the result is known to be non-empty.
+bool S2BooleanOperation::Impl::CrossingProcessor::ProcessEdge0(
+ ShapeEdgeId a_id, const S2Shape::Edge& a, CrossingIterator* it) {
+ S2_DCHECK_EQ(a.v0, a.v1);
+ // When a region is inverted, all points and polylines are discarded.
+ if (invert_a_ != invert_result_) {
+ SkipCrossings(a_id, it);
+ return true;
+ }
+ PointCrossingResult r = ProcessPointCrossings(a_id, a.v0, it);
+
+ // "contained" indicates whether the current point is inside the polygonal
+ // interior of the opposite region, using semi-open boundaries.
+ bool contained = inside_ ^ invert_b_;
+ if (r.matches_polygon && polygon_model_ != PolygonModel::SEMI_OPEN) {
+ contained = (polygon_model_ == PolygonModel::CLOSED);
+ }
+ if (r.matches_polyline) contained = true;
+
+ // The output of UNION includes duplicate values, so ensure that points are
+ // not suppressed by other points.
+ if (r.matches_point && !is_union_) contained = true;
+
+ // Test whether the point is contained after region B is inverted.
+ if (contained == invert_b_) return true; // Don't exit early.
+ return AddPointEdge(a.v0, 0);
+}
+
+// Skip any crossings that were not needed to determine the result.
+inline void S2BooleanOperation::Impl::CrossingProcessor::SkipCrossings(
+ ShapeEdgeId a_id, CrossingIterator* it) {
+ while (!it->Done(a_id)) it->Next();
+}
+
+// Returns a summary of the relationship between a point from region A and
+// a set of crossing edges from region B (see PointCrossingResult).
+S2BooleanOperation::Impl::CrossingProcessor::PointCrossingResult
+S2BooleanOperation::Impl::CrossingProcessor::ProcessPointCrossings(
+ ShapeEdgeId a_id, const S2Point& a0, CrossingIterator* it) const {
+ PointCrossingResult r;
+ for (; !it->Done(a_id); it->Next()) {
+ if (it->b_dimension() == 0) {
+ r.matches_point = true;
+ } else if (it->b_dimension() == 1) {
+ if (PolylineEdgeContainsVertex(a0, *it)) {
+ r.matches_polyline = true;
+ }
+ } else {
+ r.matches_polygon = true;
+ }
+ }
+ return r;
+}
+
+// EdgeCrossingResult describes the relationship between an edge from region A
+// ("a_edge") and a set of crossing edges from region B. For example,
+// "matches_polygon" indicates whether "a_edge" matches a polygon edge from
+// region B.
+struct S2BooleanOperation::Impl::CrossingProcessor::EdgeCrossingResult {
+ EdgeCrossingResult()
+ : matches_polyline(false), matches_polygon(false), matches_sibling(false),
+ a0_matches_polyline(false), a1_matches_polyline(false),
+ a0_matches_polygon(false), a1_matches_polygon(false),
+ a0_crossings(0), a1_crossings(0), interior_crossings(0) {
+ }
+ // These fields indicate that "a_edge" exactly matches an edge of B.
+ bool matches_polyline; // Matches polyline edge (either direction).
+ bool matches_polygon; // Matches polygon edge (same direction).
+ bool matches_sibling; // Matches polygon edge (reverse direction).
+
+ // These fields indicate that a vertex of "a_edge" matches a polyline vertex
+ // of B *and* the polyline contains that vertex.
+ bool a0_matches_polyline; // Start vertex matches contained polyline vertex.
+ bool a1_matches_polyline; // End vertex matches contained polyline vertex.
+
+ // These fields indicate that a vertex of "a_edge" matches a polygon vertex
+ // of B. (Unlike with polylines, the polygon may not contain that vertex.)
+ bool a0_matches_polygon; // Start vertex matches polygon vertex.
+ bool a1_matches_polygon; // End vertex matches polygon vertex.
+
+ // These fields count the number of edge crossings at the start vertex, end
+ // vertex, and interior of "a_edge".
+ int a0_crossings; // Count of polygon crossings at start vertex.
+ int a1_crossings; // Count of polygon crossings at end vertex.
+ int interior_crossings; // Count of polygon crossings in edge interior.
+};
+
+// Processes an edge of dimension 1 (i.e., a polyline edge) from region A.
+//
+// Supports "early exit" in the case of boolean results by returning false
+// as soon as the result is known to be non-empty.
+bool S2BooleanOperation::Impl::CrossingProcessor::ProcessEdge1(
+ ShapeEdgeId a_id, const S2Shape::Edge& a, CrossingIterator* it) {
+ // When a region is inverted, all points and polylines are discarded.
+ if (invert_a_ != invert_result_) {
+ SkipCrossings(a_id, it);
+ return true;
+ }
+ // Evaluate whether the start vertex should belong to the output, in case it
+ // needs to be emitted as an isolated vertex.
+ EdgeCrossingResult r = ProcessEdgeCrossings(a_id, a, it);
+ bool a0_inside = IsPolylineVertexInside(r.a0_matches_polyline,
+ r.a0_matches_polygon);
+
+ // Test whether the entire polyline edge should be emitted (or not emitted)
+ // because it matches a polyline or polygon edge.
+ inside_ ^= (r.a0_crossings & 1);
+ if (inside_ != IsPolylineEdgeInside(r)) {
+ inside_ ^= true; // Invert the inside_ state.
+ ++r.a1_crossings; // Restore the correct (semi-open) state later.
+ }
+
+ // If neither edge adjacent to v0 was emitted, and this polyline contains
+ // v0, and the other region contains v0, then emit an isolated vertex.
+ if (!polyline_loops_have_boundaries_ && a_id.edge_id == chain_start_ &&
+ a.v0 == a_shape_->chain_edge(chain_id_,
+ chain_limit_ - chain_start_ - 1).v1) {
+ // This is the first vertex of a polyline loop, so we can't decide if it
+ // is isolated until we process the last polyline edge.
+ chain_v0_emitted_ = inside_;
+ } else if (is_v0_isolated(a_id) &&
+ polyline_contains_v0(a_id.edge_id, chain_start_) && a0_inside) {
+ if (!AddPointEdge(a.v0, 1)) return false;
+ }
+
+ // Test whether the entire edge or any part of it belongs to the output.
+ if (inside_ || r.interior_crossings > 0) {
+ // Note: updates "inside_" to correspond to the state just before a1.
+ if (!AddEdge(a_id, a, 1 /*dimension*/, r.interior_crossings)) {
+ return false;
+ }
+ }
+ // Remember whether the edge portion just before "a1" was emitted, so that
+ // we can decide whether "a1" need to be emitted as an isolated vertex.
+ if (inside_) v0_emitted_max_edge_id_ = a_id.edge_id + 1;
+
+ // Verify that edge crossings are being counted correctly.
+ inside_ ^= (r.a1_crossings & 1);
+ if (it->crossings_complete()) {
+ S2_DCHECK_EQ(MakeS2ContainsPointQuery(&it->b_index()).Contains(a.v1),
+ inside_ ^ invert_b_);
+ }
+
+ // Special case to test whether the last vertex of a polyline should be
+ // emitted as an isolated vertex.
+ if (it->crossings_complete() && is_chain_last_vertex_isolated(a_id) &&
+ (polyline_model_ == PolylineModel::CLOSED ||
+ (!polyline_loops_have_boundaries_ &&
+ a.v1 == a_shape_->chain_edge(chain_id_, chain_start_).v0)) &&
+ IsPolylineVertexInside(r.a1_matches_polyline, r.a1_matches_polygon)) {
+ if (!AddPointEdge(a.v1, 1)) return false;
+ }
+ return true;
+}
+
+// Returns true if the current point being processed (which must be a polyline
+// vertex) is contained by the opposite region (after inversion if "invert_b_"
+// is true). "matches_polyline" and "matches_polygon" indicate whether the
+// vertex matches a polyline/polygon vertex of the opposite region.
+bool S2BooleanOperation::Impl::CrossingProcessor::IsPolylineVertexInside(
+ bool matches_polyline, bool matches_polygon) const {
+ // "contained" indicates whether the current point is inside the polygonal
+ // interior of the opposite region using semi-open boundaries.
+ bool contained = inside_ ^ invert_b_;
+
+ // For UNION the output includes duplicate polylines. The test below
+ // ensures that isolated polyline vertices are not suppressed by other
+ // polyline vertices in the output.
+ if (matches_polyline && !is_union_) {
+ contained = true;
+ } else if (matches_polygon && polygon_model_ != PolygonModel::SEMI_OPEN) {
+ contained = (polygon_model_ == PolygonModel::CLOSED);
+ }
+ // Finally, invert the result if the opposite region should be inverted.
+ return contained ^ invert_b_;
+}
+
+// Returns true if the current polyline edge is contained by the opposite
+// region (after inversion if "invert_b_" is true).
+inline bool S2BooleanOperation::Impl::CrossingProcessor::IsPolylineEdgeInside(
+ const EdgeCrossingResult& r) const {
+ // "contained" indicates whether the current point is inside the polygonal
+ // interior of the opposite region using semi-open boundaries.
+ bool contained = inside_ ^ invert_b_;
+ if (r.matches_polyline && !is_union_) {
+ contained = true;
+ } else if (r.matches_polygon) {
+ // In the SEMI_OPEN model, polygon sibling pairs cancel each other and
+ // have no effect on point or edge containment.
+ if (!(r.matches_sibling && polygon_model_ == PolygonModel::SEMI_OPEN)) {
+ contained = (polygon_model_ != PolygonModel::OPEN);
+ }
+ } else if (r.matches_sibling) {
+ contained = (polygon_model_ == PolygonModel::CLOSED);
+ }
+ // Finally, invert the result if the opposite region should be inverted.
+ return contained ^ invert_b_;
+}
+
+// Processes an edge of dimension 2 (i.e., a polygon edge) from region A.
+//
+// Supports "early exit" in the case of boolean results by returning false
+// as soon as the result is known to be non-empty.
+bool S2BooleanOperation::Impl::CrossingProcessor::ProcessEdge2(
+ ShapeEdgeId a_id, const S2Shape::Edge& a, CrossingIterator* it) {
+ // In order to keep only one copy of any shared polygon edges, we only
+ // output shared edges when processing the second region.
+ bool emit_shared = (a_region_id_ == 1);
+
+ // Degeneracies such as isolated vertices and sibling pairs can only be
+ // created by intersecting CLOSED polygons or unioning OPEN polygons.
+ bool emit_degenerate =
+ (polygon_model_ == PolygonModel::CLOSED && !invert_a_ && !invert_b_) ||
+ (polygon_model_ == PolygonModel::OPEN && invert_a_ && invert_b_);
+
+ EdgeCrossingResult r = ProcessEdgeCrossings(a_id, a, it);
+ S2_DCHECK(!r.matches_polyline);
+ inside_ ^= (r.a0_crossings & 1);
+
+ // If only one region is inverted, matching/sibling relations are reversed.
+ // TODO(ericv): Update the following code to handle degenerate loops.
+ S2_DCHECK(!r.matches_polygon || !r.matches_sibling);
+ if (invert_a_ != invert_b_) swap(r.matches_polygon, r.matches_sibling);
+
+ // Test whether the entire polygon edge should be emitted (or not emitted)
+ // because it matches a polygon edge or its sibling.
+ bool new_inside = inside_;
+
+ // Shared edge are emitted only while processing the second region.
+ if (r.matches_polygon) new_inside = emit_shared;
+
+ // Sibling pairs are emitted only when degeneracies are desired.
+ if (r.matches_sibling) new_inside = emit_degenerate;
+ if (inside_ != new_inside) {
+ inside_ ^= true; // Invert the inside_ state.
+ ++r.a1_crossings; // Restore the correct (semi-open) state later.
+ }
+
+ // Test whether the first vertex of this edge should be emitted as an
+ // isolated degenerate vertex.
+ if (a_id.edge_id == chain_start_) {
+ chain_v0_emitted_ = inside_;
+ } else if (emit_shared && emit_degenerate && r.a0_matches_polygon &&
+ is_v0_isolated(a_id)) {
+ if (!AddPointEdge(a.v0, 2)) return false;
+ }
+
+ // Test whether the entire edge or any part of it belongs to the output.
+ if (inside_ || r.interior_crossings > 0) {
+ // Note: updates "inside_" to correspond to the state just before a1.
+ if (!AddEdge(a_id, a, 2 /*dimension*/, r.interior_crossings)) {
+ return false;
+ }
+ }
+
+ // Remember whether the edge portion just before "a1" was emitted, so that
+ // we can decide whether "a1" need to be emitted as an isolated vertex.
+ if (inside_) v0_emitted_max_edge_id_ = a_id.edge_id + 1;
+ inside_ ^= (r.a1_crossings & 1);
+
+ // Verify that edge crossings are being counted correctly.
+ if (it->crossings_complete()) {
+ S2_DCHECK_EQ(MakeS2ContainsPointQuery(&it->b_index()).Contains(a.v1),
+ inside_ ^ invert_b_);
+ }
+
+ // Special case to test whether the last vertex of a loop should be emitted
+ // as an isolated degenerate vertex.
+ if (emit_shared && emit_degenerate && r.a1_matches_polygon &&
+ it->crossings_complete() && is_chain_last_vertex_isolated(a_id)) {
+ if (!AddPointEdge(a.v1, 2)) return false;
+ }
+ return true;
+}
+
+// Returns a summary of the relationship between a test edge from region A and
+// a set of crossing edges from region B (see EdgeCrossingResult).
+//
+// NOTE(ericv): We could save a bit of work when matching polygon vertices by
+// passing in a flag saying whether this information is needed. For example
+// if is only needed in ProcessEdge2 when (emit_shared && emit_degenerate).
+S2BooleanOperation::Impl::CrossingProcessor::EdgeCrossingResult
+S2BooleanOperation::Impl::CrossingProcessor::ProcessEdgeCrossings(
+ ShapeEdgeId a_id, const S2Shape::Edge& a, CrossingIterator* it) {
+ EdgeCrossingResult r;
+ if (it->Done(a_id)) return r;
+
+ // TODO(ericv): bool a_degenerate = (a.v0 == a.v1);
+ for (; !it->Done(a_id); it->Next()) {
+ // Polylines and polygons are not affected by point geometry.
+ if (it->b_dimension() == 0) continue;
+ S2Shape::Edge b = it->b_edge();
+ if (it->is_interior_crossing()) {
+ // The crossing occurs in the edge interior. The condition below says
+ // that (1) polyline crossings don't affect polygon output, and (2)
+ // subtracting a crossing polyline from a polyline has no effect.
+ if (a_dimension_ <= it->b_dimension() &&
+ !(invert_b_ != invert_result_ && it->b_dimension() == 1)) {
+ SourceId src_id(b_region_id_, it->b_shape_id(), it->b_edge_id());
+ AddCrossing(make_pair(src_id, it->left_to_right()));
+ }
+ r.interior_crossings += (it->b_dimension() == 1) ? 2 : 1;
+ } else if (it->b_dimension() == 1) {
+ // Polygons are not affected by polyline geometry.
+ if (a_dimension_ == 2) continue;
+ if ((a.v0 == b.v0 && a.v1 == b.v1) || (a.v0 == b.v1 && a.v1 == b.v0)) {
+ r.matches_polyline = true;
+ }
+ if ((a.v0 == b.v0 || a.v0 == b.v1) &&
+ PolylineEdgeContainsVertex(a.v0, *it)) {
+ r.a0_matches_polyline = true;
+ }
+ if ((a.v1 == b.v0 || a.v1 == b.v1) &&
+ PolylineEdgeContainsVertex(a.v1, *it)) {
+ r.a1_matches_polyline = true;
+ }
+ } else {
+ S2_DCHECK_EQ(2, it->b_dimension());
+ if (a.v0 == b.v0 && a.v1 == b.v1) {
+ ++r.a0_crossings;
+ r.matches_polygon = true;
+ } else if (a.v0 == b.v1 && a.v1 == b.v0) {
+ ++r.a0_crossings;
+ r.matches_sibling = true;
+ } else if (it->is_vertex_crossing()) {
+ if (a.v0 == b.v0 || a.v0 == b.v1) {
+ ++r.a0_crossings;
+ } else {
+ ++r.a1_crossings;
+ }
+ }
+ if (a.v0 == b.v0 || a.v0 == b.v1) {
+ r.a0_matches_polygon = true;
+ }
+ if (a.v1 == b.v0 || a.v1 == b.v1) {
+ r.a1_matches_polygon = true;
+ }
+ }
+ }
+ return r;
+}
+
+// Returns true if the vertex "v" is contained by the polyline edge referred
+// to by the CrossingIterator "it", taking into account the PolylineModel.
+//
+// REQUIRES: it.b_dimension() == 1
+// REQUIRES: "v" is an endpoint of it.b_edge()
+bool S2BooleanOperation::Impl::CrossingProcessor::PolylineEdgeContainsVertex(
+ const S2Point& v, const CrossingIterator& it) const {
+ S2_DCHECK_EQ(1, it.b_dimension());
+ S2_DCHECK(it.b_edge().v0 == v || it.b_edge().v1 == v);
+
+ // Closed polylines contain all their vertices.
+ if (polyline_model_ == PolylineModel::CLOSED) return true;
+
+ // Note that the code below is structured so that it.b_edge() is not usually
+ // needed (since accessing the edge can be relatively expensive).
+ const auto& b_chain = it.b_chain_info();
+ int b_edge_id = it.b_edge_id();
+
+ // The last polyline vertex is never contained. (For polyline loops, it is
+ // sufficient to treat the first vertex as begin contained.) This case also
+ // handles degenerate polylines (polylines with one edge where v0 == v1),
+ // which do not contain any points.
+ if (b_edge_id == b_chain.limit - 1 && v == it.b_edge().v1) return false;
+
+ // Otherwise all interior vertices are contained. The first polyline
+ // vertex is contained if either the polyline model is not OPEN, or the
+ // polyline forms a loop and polyline_loops_have_boundaries_ is false.
+ if (polyline_contains_v0(b_edge_id, b_chain.start)) return true;
+ if (v != it.b_edge().v0) return true;
+ if (polyline_loops_have_boundaries_) return false;
+ return v == it.b_shape().chain_edge(b_chain.chain_id,
+ b_chain.limit - b_chain.start - 1).v1;
+}
+
+// Translates the temporary representation of crossing edges (SourceId) into
+// the format expected by EdgeClippingLayer (InputEdgeId).
+void S2BooleanOperation::Impl::CrossingProcessor::DoneBoundaryPair() {
+ // Add entries that translate the "special" crossings.
+ source_id_map_[SourceId(kSetInside)] = kSetInside;
+ source_id_map_[SourceId(kSetInvertB)] = kSetInvertB;
+ source_id_map_[SourceId(kSetReverseA)] = kSetReverseA;
+ input_crossings_->reserve(input_crossings_->size() +
+ source_edge_crossings_.size());
+ for (const auto& tmp : source_edge_crossings_) {
+ auto it = source_id_map_.find(tmp.second.first);
+ S2_DCHECK(it != source_id_map_.end());
+ input_crossings_->push_back(make_pair(
+ tmp.first, CrossingInputEdge(it->second, tmp.second.second)));
+ }
+ source_edge_crossings_.clear();
+ source_id_map_.clear();
+}
+
+// Clips the boundary of A to the interior of the opposite region B and adds
+// the resulting edges to the output. Optionally, any combination of region
+// A, region B, and the result may be inverted, which allows operations such
+// as union and difference to be implemented.
+//
+// Note that when an input region is inverted with respect to the output
+// (e.g., invert_a != invert_result), all polygon edges are reversed and all
+// points and polylines are discarded, since the complement of such objects
+// cannot be represented. (If you want to compute the complement of points
+// or polylines, you can use S2LaxPolygonShape to represent your geometry as
+// degenerate polygons instead.)
+//
+// This method must be called an even number of times (first to clip A to B
+// and then to clip B to A), calling DoneBoundaryPair() after each pair.
+//
+// Supports "early exit" in the case of boolean results by returning false
+// as soon as the result is known to be non-empty.
+bool S2BooleanOperation::Impl::AddBoundary(
+ int a_region_id, bool invert_a, bool invert_b, bool invert_result,
+ const vector<ShapeEdgeId>& a_chain_starts, CrossingProcessor* cp) {
+ const S2ShapeIndex& a_index = *op_->regions_[a_region_id];
+ const S2ShapeIndex& b_index = *op_->regions_[1 - a_region_id];
+ if (!GetIndexCrossings(a_region_id)) return false;
+ cp->StartBoundary(a_region_id, invert_a, invert_b, invert_result);
+
+ // Walk the boundary of region A and build a list of all edge crossings.
+ // We also keep track of whether the current vertex is inside region B.
+ auto next_start = a_chain_starts.begin();
+ CrossingIterator next_crossing(&b_index, &index_crossings_,
+ true /*crossings_complete*/);
+ ShapeEdgeId next_id = min(*next_start, next_crossing.a_id());
+ while (next_id != kSentinel) {
+ int a_shape_id = next_id.shape_id;
+ const S2Shape& a_shape = *a_index.shape(a_shape_id);
+ cp->StartShape(&a_shape);
+ while (next_id.shape_id == a_shape_id) {
+ // TODO(ericv): Special handling of dimension 0? Can omit most of this
+ // code, including the loop, since all chains are of length 1.
+ int edge_id = next_id.edge_id;
+ S2Shape::ChainPosition chain_position = a_shape.chain_position(edge_id);
+ int chain_id = chain_position.chain_id;
+ S2Shape::Chain chain = a_shape.chain(chain_id);
+ bool start_inside = (next_id == *next_start);
+ if (start_inside) ++next_start;
+ cp->StartChain(chain_id, chain, start_inside);
+ int chain_limit = chain.start + chain.length;
+ while (edge_id < chain_limit) {
+ ShapeEdgeId a_id(a_shape_id, edge_id);
+ S2_DCHECK(cp->inside() || next_crossing.a_id() == a_id);
+ if (!cp->ProcessEdge(a_id, &next_crossing)) {
+ return false;
+ }
+ if (cp->inside()) {
+ ++edge_id;
+ } else if (next_crossing.a_id().shape_id == a_shape_id &&
+ next_crossing.a_id().edge_id < chain_limit) {
+ edge_id = next_crossing.a_id().edge_id;
+ } else {
+ break;
+ }
+ }
+ next_id = min(*next_start, next_crossing.a_id());
+ }
+ }
+ return true;
+}
+
+// Returns the first edge of each edge chain from "a_region_id" whose first
+// vertex is contained by opposite region's polygons (using the semi-open
+// boundary model). Each input region and the result region are inverted as
+// specified (invert_a, invert_b, and invert_result) before testing for
+// containment. The algorithm uses these "chain starts" in order to clip the
+// boundary of A to the interior of B in an output-senstive way.
+//
+// This method supports "early exit" in the case where a boolean predicate is
+// being evaluated and the algorithm discovers that the result region will be
+// non-empty.
+bool S2BooleanOperation::Impl::GetChainStarts(
+ int a_region_id, bool invert_a, bool invert_b, bool invert_result,
+ CrossingProcessor* cp, vector<ShapeEdgeId>* chain_starts) {
+ const S2ShapeIndex& a_index = *op_->regions_[a_region_id];
+ const S2ShapeIndex& b_index = *op_->regions_[1 - a_region_id];
+
+ if (is_boolean_output()) {
+ // If boolean output is requested, then we use the CrossingProcessor to
+ // determine whether the first edge of each chain will be emitted to the
+ // output region. This lets us terminate the operation early in many
+ // cases.
+ cp->StartBoundary(a_region_id, invert_a, invert_b, invert_result);
+ }
+
+ // If region B has no two-dimensional shapes and is not inverted, then by
+ // definition no chain starts are contained. However if boolean output is
+ // requested then we check for containment anyway, since as a side effect we
+ // may discover that the result region is non-empty and terminate the entire
+ // operation early.
+ bool b_has_interior = HasInterior(b_index);
+ if (b_has_interior || invert_b || is_boolean_output()) {
+ auto query = MakeS2ContainsPointQuery(&b_index);
+ int num_shape_ids = a_index.num_shape_ids();
+ for (int shape_id = 0; shape_id < num_shape_ids; ++shape_id) {
+ S2Shape* a_shape = a_index.shape(shape_id);
+ if (a_shape == nullptr) continue;
+
+ // If region A is being subtracted from region B, points and polylines
+ // in region A can be ignored since these shapes never contribute to the
+ // output (they can only remove edges from region B).
+ if (invert_a != invert_result && a_shape->dimension() < 2) continue;
+
+ if (is_boolean_output()) cp->StartShape(a_shape);
+ int num_chains = a_shape->num_chains();
+ for (int chain_id = 0; chain_id < num_chains; ++chain_id) {
+ S2Shape::Chain chain = a_shape->chain(chain_id);
+ if (chain.length == 0) continue;
+ ShapeEdge a(shape_id, chain.start, a_shape->chain_edge(chain_id, 0));
+ bool inside = (b_has_interior && query.Contains(a.v0())) != invert_b;
+ if (inside) {
+ chain_starts->push_back(ShapeEdgeId(shape_id, chain.start));
+ }
+ if (is_boolean_output()) {
+ cp->StartChain(chain_id, chain, inside);
+ if (!ProcessIncidentEdges(a, &query, cp)) return false;
+ }
+ }
+ }
+ }
+ chain_starts->push_back(kSentinel);
+ return true;
+}
+
+bool S2BooleanOperation::Impl::ProcessIncidentEdges(
+ const ShapeEdge& a, S2ContainsPointQuery<S2ShapeIndex>* query,
+ CrossingProcessor* cp) {
+ tmp_crossings_.clear();
+ query->VisitIncidentEdges(a.v0(), [&a, this](const ShapeEdge& b) {
+ return AddIndexCrossing(a, b, false /*is_interior*/, &tmp_crossings_);
+ });
+ // Fast path for the common case where there are no incident edges. We
+ // return false (terminating early) if the first chain edge will be emitted.
+ if (tmp_crossings_.empty()) {
+ return !cp->inside();
+ }
+ // Otherwise we invoke the full CrossingProcessor logic to determine whether
+ // the first chain edge will be emitted.
+ if (tmp_crossings_.size() > 1) {
+ std::sort(tmp_crossings_.begin(), tmp_crossings_.end());
+ // VisitIncidentEdges() should not generate any duplicate values.
+ S2_DCHECK(std::adjacent_find(tmp_crossings_.begin(), tmp_crossings_.end()) ==
+ tmp_crossings_.end());
+ }
+ tmp_crossings_.push_back(IndexCrossing(kSentinel, kSentinel));
+ CrossingIterator next_crossing(&query->index(), &tmp_crossings_,
+ false /*crossings_complete*/);
+ return cp->ProcessEdge(a.id(), &next_crossing);
+}
+
+bool S2BooleanOperation::Impl::HasInterior(const S2ShapeIndex& index) {
+ for (int s = index.num_shape_ids(); --s >= 0; ) {
+ S2Shape* shape = index.shape(s);
+ if (shape && shape->dimension() == 2) return true;
+ }
+ return false;
+}
+
+inline bool S2BooleanOperation::Impl::AddIndexCrossing(
+ const ShapeEdge& a, const ShapeEdge& b, bool is_interior,
+ IndexCrossings* crossings) {
+ crossings->push_back(IndexCrossing(a.id(), b.id()));
+ IndexCrossing* crossing = &crossings->back();
+ if (is_interior) {
+ crossing->is_interior_crossing = true;
+ if (s2pred::Sign(a.v0(), a.v1(), b.v0()) > 0) {
+ crossing->left_to_right = true;
+ }
+ } else {
+ // TODO(ericv): This field isn't used unless one shape is a polygon and
+ // the other is a polyline or polygon, but we don't have the shape
+ // dimension information readily available here.
+ if (S2::VertexCrossing(a.v0(), a.v1(), b.v0(), b.v1())) {
+ crossing->is_vertex_crossing = true;
+ }
+ }
+ return true; // Continue visiting.
+}
+
+// Initialize index_crossings_ to the set of crossing edge pairs such that the
+// first element of each pair is an edge from "region_id".
+//
+// Supports "early exit" in the case of boolean results by returning false
+// as soon as the result is known to be non-empty.
+bool S2BooleanOperation::Impl::GetIndexCrossings(int region_id) {
+ if (region_id == index_crossings_first_region_id_) return true;
+ if (index_crossings_first_region_id_ < 0) {
+ S2_DCHECK_EQ(region_id, 0); // For efficiency, not correctness.
+ if (!s2shapeutil::VisitCrossingEdgePairs(
+ *op_->regions_[0], *op_->regions_[1],
+ s2shapeutil::CrossingType::ALL,
+ [this](const ShapeEdge& a, const ShapeEdge& b, bool is_interior) {
+ // For all supported operations (union, intersection, and
+ // difference), if the input edges have an interior crossing
+ // then the output is guaranteed to have at least one edge.
+ if (is_interior && is_boolean_output()) return false;
+ return AddIndexCrossing(a, b, is_interior, &index_crossings_);
+ })) {
+ return false;
+ }
+ if (index_crossings_.size() > 1) {
+ std::sort(index_crossings_.begin(), index_crossings_.end());
+ index_crossings_.erase(
+ std::unique(index_crossings_.begin(), index_crossings_.end()),
+ index_crossings_.end());
+ }
+ // Add a sentinel value to simplify the loop logic.
+ index_crossings_.push_back(IndexCrossing(kSentinel, kSentinel));
+ index_crossings_first_region_id_ = 0;
+ }
+ if (region_id != index_crossings_first_region_id_) {
+ for (auto& crossing : index_crossings_) {
+ swap(crossing.a, crossing.b);
+ // The following predicates get inverted when the edges are swapped.
+ crossing.left_to_right ^= true;
+ crossing.is_vertex_crossing ^= true;
+ }
+ std::sort(index_crossings_.begin(), index_crossings_.end());
+ index_crossings_first_region_id_ = region_id;
+ }
+ return true;
+}
+
+// Supports "early exit" in the case of boolean results by returning false
+// as soon as the result is known to be non-empty.
+bool S2BooleanOperation::Impl::AddBoundaryPair(
+ bool invert_a, bool invert_b, bool invert_result, CrossingProcessor* cp) {
+ // Optimization: if the operation is DIFFERENCE or SYMMETRIC_DIFFERENCE,
+ // it is worthwhile checking whether the two regions are identical (in which
+ // case the output is empty).
+ //
+ // TODO(ericv): When boolean output is requested there are other quick
+ // checks that could be done here, such as checking whether a full cell from
+ // one S2ShapeIndex intersects a non-empty cell of the other S2ShapeIndex.
+ auto type = op_->op_type();
+ if (type == OpType::DIFFERENCE || type == OpType::SYMMETRIC_DIFFERENCE) {
+ if (AreRegionsIdentical()) return true;
+ } else if (!is_boolean_output()) {
+ }
+ vector<ShapeEdgeId> a_starts, b_starts;
+ if (!GetChainStarts(0, invert_a, invert_b, invert_result, cp, &a_starts) ||
+ !GetChainStarts(1, invert_b, invert_a, invert_result, cp, &b_starts) ||
+ !AddBoundary(0, invert_a, invert_b, invert_result, a_starts, cp) ||
+ !AddBoundary(1, invert_b, invert_a, invert_result, b_starts, cp)) {
+ return false;
+ }
+ if (!is_boolean_output()) cp->DoneBoundaryPair();
+ return true;
+}
+
+// Supports "early exit" in the case of boolean results by returning false
+// as soon as the result is known to be non-empty.
+bool S2BooleanOperation::Impl::BuildOpType(OpType op_type) {
+ // CrossingProcessor does the real work of emitting the output edges.
+ CrossingProcessor cp(op_->options_.polygon_model(),
+ op_->options_.polyline_model(),
+ op_->options_.polyline_loops_have_boundaries(),
+ builder_.get(), &input_dimensions_, &input_crossings_);
+ switch (op_type) {
+ case OpType::UNION:
+ // A | B == ~(~A & ~B)
+ return AddBoundaryPair(true, true, true, &cp);
+
+ case OpType::INTERSECTION:
+ // A & B
+ return AddBoundaryPair(false, false, false, &cp);
+
+ case OpType::DIFFERENCE:
+ // A - B = A & ~B
+ return AddBoundaryPair(false, true, false, &cp);
+
+ case OpType::SYMMETRIC_DIFFERENCE:
+ // Compute the union of (A - B) and (B - A).
+ return (AddBoundaryPair(false, true, false, &cp) &&
+ AddBoundaryPair(true, false, false, &cp));
+ }
+ S2_LOG(FATAL) << "Invalid S2BooleanOperation::OpType";
+ return false;
+}
+
+// Returns a bit mask indicating which of the 6 S2 cube faces intersect the
+// index contents.
+uint8 GetFaceMask(const S2ShapeIndex& index) {
+ uint8 mask = 0;
+ S2ShapeIndex::Iterator it(&index, S2ShapeIndex::BEGIN);
+ while (!it.done()) {
+ int face = it.id().face();
+ mask |= 1 << face;
+ it.Seek(S2CellId::FromFace(face + 1).range_min());
+ }
+ return mask;
+}
+
+// Given a polygon edge graph containing only degenerate edges and sibling edge
+// pairs, the purpose of this function is to decide whether the polygon is empty
+// or full except for the degeneracies, i.e. whether the degeneracies represent
+// shells or holes.
+bool S2BooleanOperation::Impl::IsFullPolygonResult(
+ const S2Builder::Graph& g, S2Error* error) const {
+ // If there are no edges of dimension 2, the result could be either the
+ // empty polygon or the full polygon. Note that this is harder to determine
+ // than you might think due to snapping. For example, the union of two
+ // non-empty polygons can be empty, because both polygons consist of tiny
+ // loops that are eliminated by snapping. Similarly, even if two polygons
+ // both contain a common point their intersection can still be empty.
+ //
+ // We distinguish empty from full results using two heuristics:
+ //
+ // 1. We compute a bit mask representing the subset of the six S2 cube faces
+ // intersected by each input geometry, and use this to determine if only
+ // one of the two results is possible. (This test is very fast.) Note
+ // that snapping will never cause the result to cover an entire extra
+ // cube face because the maximum allowed snap radius is too small.
+ S2_DCHECK_LE(S2Builder::SnapFunction::kMaxSnapRadius().degrees(), 70);
+ //
+ // 2. We compute the area of each input geometry, and use this to bound the
+ // minimum and maximum area of the result. If only one of {0, 4*Pi} is
+ // possible then we are done. If neither is possible then we choose the
+ // one that is closest to being possible (since snapping can change the
+ // result area). Both results are possible only when computing the
+ // symmetric difference of two regions of area 2*Pi each, in which case we
+ // must resort to additional heuristics (see below).
+ //
+ // TODO(ericv): Implement a predicate that uses the results of edge snapping
+ // directly, rather than computing areas. This would not only be much faster
+ // but would also allows all cases to be handled 100% robustly.
+ const S2ShapeIndex& a = *op_->regions_[0];
+ const S2ShapeIndex& b = *op_->regions_[1];
+ switch (op_->op_type()) {
+ case OpType::UNION:
+ return IsFullPolygonUnion(a, b);
+
+ case OpType::INTERSECTION:
+ return IsFullPolygonIntersection(a, b);
+
+ case OpType::DIFFERENCE:
+ return IsFullPolygonDifference(a, b);
+
+ case OpType::SYMMETRIC_DIFFERENCE:
+ return IsFullPolygonSymmetricDifference(a, b);
+
+ default:
+ S2_LOG(FATAL) << "Invalid S2BooleanOperation::OpType";
+ return false;
+ }
+}
+
+bool S2BooleanOperation::Impl::IsFullPolygonUnion(
+ const S2ShapeIndex& a, const S2ShapeIndex& b) const {
+ // See comments in IsFullPolygonResult(). The most common case is that
+ // neither input polygon is empty but the result is empty due to snapping.
+
+ // The result can be full only if the union of the two input geometries
+ // intersects all six faces of the S2 cube. This test is fast.
+ if ((GetFaceMask(a) | GetFaceMask(b)) != kAllFacesMask) return false;
+
+ // The union area satisfies:
+ //
+ // max(A, B) <= Union(A, B) <= min(4*Pi, A + B)
+ //
+ // where A, B can refer to a polygon or its area. We then choose the result
+ // that assumes the smallest amount of error.
+ double a_area = S2::GetArea(a), b_area = S2::GetArea(b);
+ double min_area = max(a_area, b_area);
+ double max_area = min(4 * M_PI, a_area + b_area);
+ return min_area > 4 * M_PI - max_area;
+}
+
+bool S2BooleanOperation::Impl::IsFullPolygonIntersection(
+ const S2ShapeIndex& a, const S2ShapeIndex& b) const {
+ // See comments in IsFullPolygonResult(). By far the most common case is
+ // that the result is empty.
+
+ // The result can be full only if each of the two input geometries
+ // intersects all six faces of the S2 cube. This test is fast.
+ if ((GetFaceMask(a) & GetFaceMask(b)) != kAllFacesMask) return false;
+
+ // The intersection area satisfies:
+ //
+ // max(0, A + B - 4*Pi) <= Intersection(A, B) <= min(A, B)
+ //
+ // where A, B can refer to a polygon or its area. We then choose the result
+ // that assumes the smallest amount of error.
+ double a_area = S2::GetArea(a), b_area = S2::GetArea(b);
+ double min_area = max(0.0, a_area + b_area - 4 * M_PI);
+ double max_area = min(a_area, b_area);
+ return min_area > 4 * M_PI - max_area;
+}
+
+bool S2BooleanOperation::Impl::IsFullPolygonDifference(
+ const S2ShapeIndex& a, const S2ShapeIndex& b) const {
+ // See comments in IsFullPolygonResult(). By far the most common case is
+ // that the result is empty.
+
+ // The result can be full only if each cube face is intersected by the first
+ // geometry. (The second geometry is irrelevant, since for example it could
+ // consist of a tiny loop on each S2 cube face.) This test is fast.
+ if (GetFaceMask(a) != kAllFacesMask) return false;
+
+ // The difference area satisfies:
+ //
+ // max(0, A - B) <= Difference(A, B) <= min(A, 4*Pi - B)
+ //
+ // where A, B can refer to a polygon or its area. We then choose the result
+ // that assumes the smallest amount of error.
+ double a_area = S2::GetArea(a), b_area = S2::GetArea(b);
+ double min_area = max(0.0, a_area - b_area);
+ double max_area = min(a_area, 4 * M_PI - b_area);
+ return min_area > 4 * M_PI - max_area;
+}
+
+bool S2BooleanOperation::Impl::IsFullPolygonSymmetricDifference(
+ const S2ShapeIndex& a, const S2ShapeIndex& b) const {
+ // See comments in IsFullPolygonResult(). By far the most common case is
+ // that the result is empty.
+
+ // The result can be full only if the union of the two input geometries
+ // intersects all six faces of the S2 cube. This test is fast.
+ uint8 a_mask = GetFaceMask(a);
+ uint8 b_mask = GetFaceMask(b);
+ if ((a_mask | b_mask) != kAllFacesMask) return false;
+
+ // The symmetric difference area satisfies:
+ //
+ // |A - B| <= SymmetricDifference(A, B) <= 4*Pi - |4*Pi - (A + B)|
+ //
+ // where A, B can refer to a polygon or its area.
+ double a_area = S2::GetArea(a), b_area = S2::GetArea(b);
+ double min_area = fabs(a_area - b_area);
+ double max_area = 4 * M_PI - fabs(4 * M_PI - (a_area + b_area));
+
+ // Now we choose the result that assumes the smallest amount of error
+ // (min_area in the empty case, and (4*Pi - max_area) in the full case).
+ // However in the case of symmetric difference these two errors may be equal,
+ // meaning that the result is ambiguous. This happens when both polygons have
+ // area 2*Pi. Furthermore, this can happen even when the areas are not
+ // exactly 2*Pi due to snapping and area calculation errors.
+ //
+ // To determine whether the result is ambiguous, we compute a rough estimate
+ // of the maximum expected area error (including errors due to snapping),
+ // using the worst-case error bound for a hemisphere defined by 4 vertices.
+ auto edge_snap_radius = op_->options_.snap_function().snap_radius() +
+ S2::kIntersectionError; // split_crossing_edges
+ double hemisphere_area_error = 2 * M_PI * edge_snap_radius.radians() +
+ 40 * DBL_EPSILON; // GetCurvatureMaxError
+
+ // The following sign is the difference between the error needed for an empty
+ // result and the error needed for a full result. It is negative if an
+ // empty result is possible, positive if a full result is possible, and zero
+ // if both results are possible.
+ double error_sign = min_area - (4 * M_PI - max_area);
+ if (fabs(error_sign) <= hemisphere_area_error) {
+ // Handling the ambiguous case correctly requires a more sophisticated
+ // algorithm (see below), but we can at least handle the simple cases by
+ // testing whether both input geometries intersect all 6 cube faces. If
+ // not, then the result is definitely full.
+ if ((a_mask & b_mask) != kAllFacesMask) return true;
+
+ // Otherwise both regions have area 2*Pi and intersect all 6 cube faces.
+ // We choose "empty" in this case under the assumption that it is more
+ // likely that the user is computing the difference between two nearly
+ // identical polygons.
+ //
+ // TODO(ericv): Implement a robust algorithm based on examining the edge
+ // snapping results directly, or alternatively add another heuristic (such
+ // as testing containment of random points, or using a larger bit mask in
+ // the tests above, e.g. a 24-bit mask representing all level 1 cells).
+ return false;
+ }
+ return error_sign > 0;
+}
+
+bool S2BooleanOperation::Impl::AreRegionsIdentical() const {
+ const S2ShapeIndex* a = op_->regions_[0];
+ const S2ShapeIndex* b = op_->regions_[1];
+ if (a == b) return true;
+ int num_shape_ids = a->num_shape_ids();
+ if (num_shape_ids != b->num_shape_ids()) return false;
+ for (int s = 0; s < num_shape_ids; ++s) {
+ const S2Shape* a_shape = a->shape(s);
+ const S2Shape* b_shape = b->shape(s);
+ if (a_shape->dimension() != b_shape->dimension()) return false;
+ if (a_shape->dimension() == 2) {
+ auto a_ref = a_shape->GetReferencePoint();
+ auto b_ref = b_shape->GetReferencePoint();
+ if (a_ref.point != b_ref.point) return false;
+ if (a_ref.contained != b_ref.contained) return false;
+ }
+ int num_chains = a_shape->num_chains();
+ if (num_chains != b_shape->num_chains()) return false;
+ for (int c = 0; c < num_chains; ++c) {
+ S2Shape::Chain a_chain = a_shape->chain(c);
+ S2Shape::Chain b_chain = b_shape->chain(c);
+ S2_DCHECK_EQ(a_chain.start, b_chain.start);
+ if (a_chain.length != b_chain.length) return false;
+ for (int i = 0; i < a_chain.length; ++i) {
+ S2Shape::Edge a_edge = a_shape->chain_edge(c, i);
+ S2Shape::Edge b_edge = b_shape->chain_edge(c, i);
+ if (a_edge.v0 != b_edge.v0) return false;
+ if (a_edge.v1 != b_edge.v1) return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool S2BooleanOperation::Impl::Build(S2Error* error) {
+ error->Clear();
+ if (is_boolean_output()) {
+ // BuildOpType() returns true if and only if the result has no edges.
+ S2Builder::Graph g; // Unused by IsFullPolygonResult() implementation.
+ *op_->result_empty_ =
+ BuildOpType(op_->op_type()) && !IsFullPolygonResult(g, error);
+ return true;
+ }
+ // TODO(ericv): Rather than having S2Builder split the edges, it would be
+ // faster to call AddVertex() in this class and have a new S2Builder
+ // option that increases the edge_snap_radius_ to account for errors in
+ // the intersection point (the way that split_crossing_edges does).
+ S2Builder::Options options(op_->options_.snap_function());
+ options.set_split_crossing_edges(true);
+
+ // TODO(ericv): Ideally idempotent() should be true, but existing clients
+ // expect vertices closer than the full "snap_radius" to be snapped.
+ options.set_idempotent(false);
+ builder_ = make_unique<S2Builder>(options);
+ builder_->StartLayer(make_unique<EdgeClippingLayer>(
+ &op_->layers_, &input_dimensions_, &input_crossings_));
+
+ // Add a predicate that decides whether a result with no polygon edges should
+ // be interpreted as the empty polygon or the full polygon.
+ builder_->AddIsFullPolygonPredicate(
+ [this](const S2Builder::Graph& g, S2Error* error) {
+ return IsFullPolygonResult(g, error);
+ });
+ (void) BuildOpType(op_->op_type());
+ return builder_->Build(error);
+}
+
+S2BooleanOperation::Options::Options()
+ : snap_function_(make_unique<s2builderutil::IdentitySnapFunction>(
+ S1Angle::Zero())) {
+}
+
+S2BooleanOperation::Options::Options(const SnapFunction& snap_function)
+ : snap_function_(snap_function.Clone()) {
+}
+
+S2BooleanOperation::Options::Options(const Options& options)
+ : snap_function_(options.snap_function_->Clone()),
+ polygon_model_(options.polygon_model_),
+ polyline_model_(options.polyline_model_),
+ polyline_loops_have_boundaries_(options.polyline_loops_have_boundaries_),
+ precision_(options.precision_),
+ conservative_output_(options.conservative_output_),
+ source_id_lexicon_(options.source_id_lexicon_) {
+}
+
+S2BooleanOperation::Options& S2BooleanOperation::Options::operator=(
+ const Options& options) {
+ snap_function_ = options.snap_function_->Clone();
+ polygon_model_ = options.polygon_model_;
+ polyline_model_ = options.polyline_model_;
+ polyline_loops_have_boundaries_ = options.polyline_loops_have_boundaries_;
+ precision_ = options.precision_;
+ conservative_output_ = options.conservative_output_;
+ source_id_lexicon_ = options.source_id_lexicon_;
+ return *this;
+}
+
+const SnapFunction& S2BooleanOperation::Options::snap_function() const {
+ return *snap_function_;
+}
+
+void S2BooleanOperation::Options::set_snap_function(
+ const SnapFunction& snap_function) {
+ snap_function_ = snap_function.Clone();
+}
+
+PolygonModel S2BooleanOperation::Options::polygon_model() const {
+ return polygon_model_;
+}
+
+void S2BooleanOperation::Options::set_polygon_model(PolygonModel model) {
+ polygon_model_ = model;
+}
+
+PolylineModel S2BooleanOperation::Options::polyline_model() const {
+ return polyline_model_;
+}
+
+void S2BooleanOperation::Options::set_polyline_model(PolylineModel model) {
+ polyline_model_ = model;
+}
+
+bool S2BooleanOperation::Options::polyline_loops_have_boundaries() const {
+ return polyline_loops_have_boundaries_;
+}
+
+void S2BooleanOperation::Options::set_polyline_loops_have_boundaries(
+ bool value) {
+ polyline_loops_have_boundaries_ = value;
+}
+
+Precision S2BooleanOperation::Options::precision() const {
+ return precision_;
+}
+
+bool S2BooleanOperation::Options::conservative_output() const {
+ return conservative_output_;
+}
+
+ValueLexicon<S2BooleanOperation::SourceId>*
+S2BooleanOperation::Options::source_id_lexicon() const {
+ return source_id_lexicon_;
+}
+
+const char* S2BooleanOperation::OpTypeToString(OpType op_type) {
+ switch (op_type) {
+ case OpType::UNION: return "UNION";
+ case OpType::INTERSECTION: return "INTERSECTION";
+ case OpType::DIFFERENCE: return "DIFFERENCE";
+ case OpType::SYMMETRIC_DIFFERENCE: return "SYMMETRIC DIFFERENCE";
+ default: return "Unknown OpType";
+ }
+}
+
+S2BooleanOperation::S2BooleanOperation(OpType op_type,
+ const Options& options)
+ : op_type_(op_type), options_(options), result_empty_(nullptr) {
+}
+
+S2BooleanOperation::S2BooleanOperation(OpType op_type, bool* result_empty,
+ const Options& options)
+ : op_type_(op_type), options_(options),
+ result_empty_(result_empty) {
+}
+
+S2BooleanOperation::S2BooleanOperation(
+ OpType op_type, unique_ptr<S2Builder::Layer> layer, const Options& options)
+ : op_type_(op_type), options_(options), result_empty_(nullptr) {
+ layers_.push_back(std::move(layer));
+}
+
+S2BooleanOperation::S2BooleanOperation(
+ OpType op_type, vector<unique_ptr<S2Builder::Layer>> layers,
+ const Options& options)
+ : op_type_(op_type), options_(options), layers_(std::move(layers)),
+ result_empty_(nullptr) {
+}
+
+bool S2BooleanOperation::Build(const S2ShapeIndex& a,
+ const S2ShapeIndex& b,
+ S2Error* error) {
+ regions_[0] = &a;
+ regions_[1] = &b;
+ return Impl(this).Build(error);
+}
+
+bool S2BooleanOperation::IsEmpty(
+ OpType op_type, const S2ShapeIndex& a, const S2ShapeIndex& b,
+ const Options& options) {
+ bool result_empty;
+ S2BooleanOperation op(op_type, &result_empty, options);
+ S2Error error;
+ op.Build(a, b, &error);
+ S2_DCHECK(error.ok());
+ return result_empty;
+}
--- /dev/null
+#include "cpp-compat.h"
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// The algorithm is based on the idea of choosing a set of sites and computing
+// their "limited radius Voronoi diagram", which is obtained by intersecting
+// each Voronoi region with a disc of fixed radius (the "snap radius")
+// centered around the corresponding site.
+//
+// For each input edge, we then determine the sequence of Voronoi regions
+// crossed by that edge, and snap the edge to the corresponding sequence of
+// sites. (In other words, each input edge is replaced by an edge chain.)
+//
+// The sites are chosen by starting with the set of input vertices, optionally
+// snapping them to discrete point set (such as S2CellId centers or lat/lng E7
+// coordinates), and then choosing a subset such that no two sites are closer
+// than the given "snap_radius". Note that the sites do not need to be spaced
+// regularly -- their positions are completely arbitrary.
+//
+// Rather than computing the full limited radius Voronoi diagram, instead we
+// compute on demand the sequence of Voronoi regions intersected by each edge.
+// We do this by first finding all the sites that are within "snap_radius" of
+// the edge, sorting them by distance from the edge origin, and then using an
+// incremental algorithm.
+//
+// We implement the minimum edge-vertex separation property by snapping all
+// the input edges and checking whether any site (the "site to avoid") would
+// then be too close to the edge. If so we add another site (the "separation
+// site") along the input edge, positioned so that the new snapped edge is
+// guaranteed to be far enough away from the site to avoid. We then find all
+// the input edges that are within "snap_radius" of the new site, and resnap
+// those edges. (It is very rare that we need to add a separation site, even
+// when sites are densely spaced.)
+//
+// Idempotency is implemented by explicitly checking whether the input
+// geometry already meets the output criteria. This is not as sophisticated
+// as Stable Snap Rounding (Hershberger); I have worked out the details and it
+// is possible to adapt those ideas here, but it would make the implementation
+// significantly more complex.
+//
+// The only way that different output layers interact is in the choice of
+// Voronoi sites:
+//
+// - Vertices from all layers contribute to the initial selection of sites.
+//
+// - Edges in any layer that pass too close to a site can cause new sites to
+// be added (which affects snapping in all layers).
+//
+// - Simplification can be thought of as removing sites. A site can be
+// removed only if the snapped edges stay within the error bounds of the
+// corresponding input edges in all layers.
+//
+// Otherwise all layers are processed independently. For example, sibling
+// edge pairs can only cancel each other within a single layer (if desired).
+
+#include "s2/s2builder.h"
+
+#include <algorithm>
+#include <cfloat>
+#include <cmath>
+#include <iostream>
+#include <memory>
+#include <numeric>
+#include <string>
+#include <vector>
+
+#include "s2/base/casts.h"
+#include "s2/base/logging.h"
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/util/bits/bits.h"
+#include "s2/id_set_lexicon.h"
+#include "s2/mutable_s2shape_index.h"
+#include "s2/s1angle.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s2builder_graph.h"
+#include "s2/s2builder_layer.h"
+#include "s2/s2builderutil_snap_functions.h"
+#include "s2/s2closest_edge_query.h"
+#include "s2/s2closest_point_query.h"
+#include "s2/s2edge_crossings.h"
+#include "s2/s2edge_distances.h"
+#include "s2/s2error.h"
+#include "s2/s2loop.h"
+#include "s2/s2point_index.h"
+#include "s2/s2pointutil.h"
+#include "s2/s2polygon.h"
+#include "s2/s2polyline.h"
+#include "s2/s2polyline_simplifier.h"
+#include "s2/s2predicates.h"
+#include "s2/s2shapeutil_visit_crossing_edge_pairs.h"
+#include "s2/s2text_format.h"
+
+using absl::make_unique;
+using gtl::compact_array;
+using std::max;
+using std::pair;
+using std::unique_ptr;
+using std::vector;
+
+// Internal flag intended to be set from within a debugger.
+bool s2builder_verbose = false;
+
+S1Angle S2Builder::SnapFunction::max_edge_deviation() const {
+ // We want max_edge_deviation() to be large enough compared to snap_radius()
+ // such that edge splitting is rare.
+ //
+ // Using spherical trigonometry, if the endpoints of an edge of length L
+ // move by at most a distance R, the center of the edge moves by at most
+ // asin(sin(R) / cos(L / 2)). Thus the (max_edge_deviation / snap_radius)
+ // ratio increases with both the snap radius R and the edge length L.
+ //
+ // We arbitrarily limit the edge deviation to be at most 10% more than the
+ // snap radius. With the maximum allowed snap radius of 70 degrees, this
+ // means that edges up to 30.6 degrees long are never split. For smaller
+ // snap radii, edges up to 49 degrees long are never split. (Edges of any
+ // length are not split unless their endpoints move far enough so that the
+ // actual edge deviation exceeds the limit; in practice, splitting is rare
+ // even with long edges.) Note that it is always possible to split edges
+ // when max_edge_deviation() is exceeded; see MaybeAddExtraSites().
+ S2_DCHECK_LE(snap_radius(), kMaxSnapRadius());
+ const double kMaxEdgeDeviationRatio = 1.1;
+ return kMaxEdgeDeviationRatio * snap_radius();
+}
+
+S2Builder::Options::Options()
+ : snap_function_(
+ make_unique<s2builderutil::IdentitySnapFunction>(S1Angle::Zero())) {
+}
+
+S2Builder::Options::Options(const SnapFunction& snap_function)
+ : snap_function_(snap_function.Clone()) {
+}
+
+S2Builder::Options::Options(const Options& options)
+ : snap_function_(options.snap_function_->Clone()),
+ split_crossing_edges_(options.split_crossing_edges_),
+ simplify_edge_chains_(options.simplify_edge_chains_),
+ idempotent_(options.idempotent_) {
+}
+
+S2Builder::Options& S2Builder::Options::operator=(const Options& options) {
+ snap_function_ = options.snap_function_->Clone();
+ split_crossing_edges_ = options.split_crossing_edges_;
+ simplify_edge_chains_ = options.simplify_edge_chains_;
+ idempotent_ = options.idempotent_;
+ return *this;
+}
+
+bool operator==(const S2Builder::GraphOptions& x,
+ const S2Builder::GraphOptions& y) {
+ return (x.edge_type() == y.edge_type() &&
+ x.degenerate_edges() == y.degenerate_edges() &&
+ x.duplicate_edges() == y.duplicate_edges() &&
+ x.sibling_pairs() == y.sibling_pairs() &&
+ x.allow_vertex_filtering() == y.allow_vertex_filtering());
+}
+
+// Helper functions for computing error bounds:
+
+static S1ChordAngle RoundUp(S1Angle a) {
+ S1ChordAngle ca(a);
+ return ca.PlusError(ca.GetS1AngleConstructorMaxError());
+}
+
+static S1ChordAngle AddPointToPointError(S1ChordAngle ca) {
+ return ca.PlusError(ca.GetS2PointConstructorMaxError());
+}
+
+static S1ChordAngle AddPointToEdgeError(S1ChordAngle ca) {
+ return ca.PlusError(S2::GetUpdateMinDistanceMaxError(ca));
+}
+
+S2Builder::S2Builder() {
+}
+
+S2Builder::S2Builder(const Options& options) {
+ Init(options);
+}
+
+void S2Builder::Init(const Options& options) {
+ options_ = options;
+ const SnapFunction& snap_function = options.snap_function();
+ S1Angle snap_radius = snap_function.snap_radius();
+ S2_DCHECK_LE(snap_radius, SnapFunction::kMaxSnapRadius());
+
+ // Convert the snap radius to an S1ChordAngle. This is the "true snap
+ // radius" used when evaluating exact predicates (s2predicates.h).
+ site_snap_radius_ca_ = S1ChordAngle(snap_radius);
+
+ // When split_crossing_edges() is true, we need to use a larger snap radius
+ // for edges than for vertices to ensure that both edges are snapped to the
+ // edge intersection location. This is because the computed intersection
+ // point is not exact; it may be up to kIntersectionError away from its true
+ // position. The computed intersection point might then be snapped to some
+ // other vertex up to snap_radius away. So to ensure that both edges are
+ // snapped to a common vertex, we need to increase the snap radius for edges
+ // to at least the sum of these two values (calculated conservatively).
+ S1Angle edge_snap_radius = snap_radius;
+ if (!options.split_crossing_edges()) {
+ edge_snap_radius_ca_ = site_snap_radius_ca_;
+ } else {
+ edge_snap_radius += S2::kIntersectionError;
+ edge_snap_radius_ca_ = RoundUp(edge_snap_radius);
+ }
+ snapping_requested_ = (edge_snap_radius > S1Angle::Zero());
+
+ // Compute the maximum distance that a vertex can be separated from an
+ // edge while still affecting how that edge is snapped.
+ max_edge_deviation_ = snap_function.max_edge_deviation();
+ edge_site_query_radius_ca_ = S1ChordAngle(
+ max_edge_deviation_ + snap_function.min_edge_vertex_separation());
+
+ // Compute the maximum edge length such that even if both endpoints move by
+ // the maximum distance allowed (i.e., snap_radius), the center of the edge
+ // will still move by less than max_edge_deviation(). This saves us a lot
+ // of work since then we don't need to check the actual deviation.
+ min_edge_length_to_split_ca_ = S1ChordAngle::Radians(
+ 2 * acos(sin(snap_radius) / sin(max_edge_deviation_)));
+
+ // If the condition below is violated, then AddExtraSites() needs to be
+ // modified to check that snapped edges pass on the same side of each "site
+ // to avoid" as the input edge. Currently it doesn't need to do this
+ // because the condition below guarantees that if the snapped edge passes on
+ // the wrong side of the site then it is also too close, which will cause a
+ // separation site to be added.
+ //
+ // Currently max_edge_deviation() is at most 1.1 * snap_radius(), whereas
+ // min_edge_vertex_separation() is at least 0.219 * snap_radius() (based on
+ // S2CellIdSnapFunction, which is currently the worst case).
+ S2_DCHECK_LE(snap_function.max_edge_deviation(),
+ snap_function.snap_radius() +
+ snap_function.min_edge_vertex_separation());
+
+ // To implement idempotency, we check whether the input geometry could
+ // possibly be the output of a previous S2Builder invocation. This involves
+ // testing whether any site/site or edge/site pairs are too close together.
+ // This is done using exact predicates, which require converting the minimum
+ // separation values to an S1ChordAngle.
+ min_site_separation_ = snap_function.min_vertex_separation();
+ min_site_separation_ca_ = S1ChordAngle(min_site_separation_);
+ min_edge_site_separation_ca_ =
+ S1ChordAngle(snap_function.min_edge_vertex_separation());
+
+ // This is an upper bound on the distance computed by S2ClosestPointQuery
+ // where the true distance might be less than min_edge_site_separation_ca_.
+ min_edge_site_separation_ca_limit_ =
+ AddPointToEdgeError(min_edge_site_separation_ca_);
+
+ // Compute the maximum possible distance between two sites whose Voronoi
+ // regions touch. (The maximum radius of each Voronoi region is
+ // edge_snap_radius_.) Then increase this bound to account for errors.
+ max_adjacent_site_separation_ca_ =
+ AddPointToPointError(RoundUp(2 * edge_snap_radius));
+
+ // Finally, we also precompute sin^2(edge_snap_radius), which is simply the
+ // squared distance between a vertex and an edge measured perpendicular to
+ // the plane containing the edge, and increase this value by the maximum
+ // error in the calculation to compare this distance against the bound.
+ double d = sin(edge_snap_radius);
+ edge_snap_radius_sin2_ = d * d;
+ edge_snap_radius_sin2_ += ((9.5 * d + 2.5 + 2 * sqrt(3.0)) * d +
+ 9 * DBL_EPSILON) * DBL_EPSILON;
+
+ // Initialize the current label set.
+ label_set_id_ = label_set_lexicon_.EmptySetId();
+ label_set_modified_ = false;
+
+ // If snapping was requested, we try to determine whether the input geometry
+ // already meets the output requirements. This is necessary for
+ // idempotency, and can also save work. If we discover any reason that the
+ // input geometry needs to be modified, snapping_needed_ is set to true.
+ snapping_needed_ = false;
+}
+
+void S2Builder::clear_labels() {
+ label_set_.clear();
+ label_set_modified_ = true;
+}
+
+void S2Builder::push_label(Label label) {
+ S2_DCHECK_GE(label, 0);
+ label_set_.push_back(label);
+ label_set_modified_ = true;
+}
+
+void S2Builder::pop_label() {
+ label_set_.pop_back();
+ label_set_modified_ = true;
+}
+
+void S2Builder::set_label(Label label) {
+ S2_DCHECK_GE(label, 0);
+ label_set_.resize(1);
+ label_set_[0] = label;
+ label_set_modified_ = true;
+}
+
+bool S2Builder::IsFullPolygonUnspecified(const S2Builder::Graph& g,
+ S2Error* error) {
+ error->Init(S2Error::BUILDER_IS_FULL_PREDICATE_NOT_SPECIFIED,
+ "A degenerate polygon was found, but no predicate was specified "
+ "to determine whether the polygon is empty or full. Call "
+ "S2Builder::AddIsFullPolygonPredicate() to fix this problem.");
+ return false; // Assumes the polygon is empty.
+}
+
+S2Builder::IsFullPolygonPredicate S2Builder::IsFullPolygon(bool is_full) {
+ return [is_full](const S2Builder::Graph& g, S2Error* error) {
+ return is_full;
+ };
+}
+
+void S2Builder::StartLayer(unique_ptr<Layer> layer) {
+ layer_options_.push_back(layer->graph_options());
+ layer_begins_.push_back(input_edges_.size());
+ layer_is_full_polygon_predicates_.push_back(IsFullPolygon(false));
+ layers_.push_back(std::move(layer));
+}
+
+// Input vertices are stored in a vector, with some removal of duplicates.
+// Edges are represented as (VertexId, VertexId) pairs. All edges are stored
+// in a single vector; each layer corresponds to a contiguous range.
+
+S2Builder::InputVertexId S2Builder::AddVertex(const S2Point& v) {
+ // Remove duplicate vertices that follow the pattern AB, BC, CD. If we want
+ // to do anything more sophisticated, either use a ValueLexicon, or sort the
+ // vertices once they have all been added, remove duplicates, and update the
+ // edges.
+ if (input_vertices_.empty() || v != input_vertices_.back()) {
+ input_vertices_.push_back(v);
+ }
+ return input_vertices_.size() - 1;
+}
+
+void S2Builder::AddEdge(const S2Point& v0, const S2Point& v1) {
+ S2_DCHECK(!layers_.empty()) << "Call StartLayer before adding any edges";
+
+ if (v0 == v1 && (layer_options_.back().degenerate_edges() ==
+ GraphOptions::DegenerateEdges::DISCARD)) {
+ return;
+ }
+ InputVertexId j0 = AddVertex(v0);
+ InputVertexId j1 = AddVertex(v1);
+ input_edges_.push_back(InputEdge(j0, j1));
+
+ // If there are any labels, then attach them to this input edge.
+ if (label_set_modified_) {
+ if (label_set_ids_.empty()) {
+ // Populate the missing entries with empty label sets.
+ label_set_ids_.assign(input_edges_.size() - 1, label_set_id_);
+ }
+ label_set_id_ = label_set_lexicon_.Add(label_set_);
+ label_set_ids_.push_back(label_set_id_);
+ label_set_modified_ = false;
+ } else if (!label_set_ids_.empty()) {
+ label_set_ids_.push_back(label_set_id_);
+ }
+}
+
+void S2Builder::AddPolyline(const S2Polyline& polyline) {
+ const int n = polyline.num_vertices();
+ for (int i = 1; i < n; ++i) {
+ AddEdge(polyline.vertex(i - 1), polyline.vertex(i));
+ }
+}
+
+void S2Builder::AddLoop(const S2Loop& loop) {
+ // Ignore loops that do not have a boundary.
+ if (loop.is_empty_or_full()) return;
+
+ // For loops that represent holes, we add the edge from vertex n-1 to vertex
+ // n-2 first. This is because these edges will be assembled into a
+ // clockwise loop, which will eventually be normalized in S2Polygon by
+ // calling S2Loop::Invert(). S2Loop::Invert() reverses the order of the
+ // vertices, so to end up with the original vertex order (0, 1, ..., n-1) we
+ // need to build a clockwise loop with vertex order (n-1, n-2, ..., 0).
+ // This is done by adding the edge (n-1, n-2) first, and then ensuring that
+ // Build() assembles loops starting from edges in the order they were added.
+ const int n = loop.num_vertices();
+ for (int i = 0; i < n; ++i) {
+ AddEdge(loop.oriented_vertex(i), loop.oriented_vertex(i + 1));
+ }
+}
+
+void S2Builder::AddPolygon(const S2Polygon& polygon) {
+ for (int i = 0; i < polygon.num_loops(); ++i) {
+ AddLoop(*polygon.loop(i));
+ }
+}
+
+void S2Builder::AddShape(const S2Shape& shape) {
+ for (int e = 0, n = shape.num_edges(); e < n; ++e) {
+ S2Shape::Edge edge = shape.edge(e);
+ AddEdge(edge.v0, edge.v1);
+ }
+}
+
+void S2Builder::AddIsFullPolygonPredicate(IsFullPolygonPredicate predicate) {
+ layer_is_full_polygon_predicates_.back() = std::move(predicate);
+}
+
+void S2Builder::ForceVertex(const S2Point& vertex) {
+ sites_.push_back(vertex);
+}
+
+// An S2Shape used to represent the entire collection of S2Builder input edges.
+// Vertices are specified as indices into a vertex vector to save space.
+namespace {
+class VertexIdEdgeVectorShape final : public S2Shape {
+ public:
+ // Requires that "edges" is constant for the lifetime of this object.
+ VertexIdEdgeVectorShape(const vector<pair<int32, int32>>& edges,
+ const vector<S2Point>& vertices)
+ : edges_(edges), vertices_(vertices) {
+ }
+
+ const S2Point& vertex0(int e) const { return vertex(edges_[e].first); }
+ const S2Point& vertex1(int e) const { return vertex(edges_[e].second); }
+
+ // S2Shape interface:
+ int num_edges() const override { return edges_.size(); }
+ Edge edge(int e) const override {
+ return Edge(vertices_[edges_[e].first], vertices_[edges_[e].second]);
+ }
+ int dimension() const override { return 1; }
+ ReferencePoint GetReferencePoint() const override {
+ return ReferencePoint::Contained(false);
+ }
+ int num_chains() const override { return edges_.size(); }
+ Chain chain(int i) const override { return Chain(i, 1); }
+ Edge chain_edge(int i, int j) const override { return edge(i); }
+ ChainPosition chain_position(int e) const override {
+ return ChainPosition(e, 0);
+ }
+
+ private:
+ const S2Point& vertex(int i) const { return vertices_[i]; }
+
+ const vector<std::pair<int32, int32>>& edges_;
+ const vector<S2Point>& vertices_;
+};
+} // namespace
+
+bool S2Builder::Build(S2Error* error) {
+ // S2_CHECK rather than S2_DCHECK because this is friendlier than crashing on the
+ // "error->ok()" call below. It would be easy to allow (error == nullptr)
+ // by declaring a local "tmp_error", but it seems better to make clients
+ // think about error handling.
+ S2_CHECK(error != nullptr);
+ error->Clear();
+ error_ = error;
+
+ // Mark the end of the last layer.
+ layer_begins_.push_back(input_edges_.size());
+
+ // See the algorithm overview at the top of this file.
+ if (snapping_requested_ && !options_.idempotent()) {
+ snapping_needed_ = true;
+ }
+ ChooseSites();
+ BuildLayers();
+ Reset();
+ return error->ok();
+}
+
+void S2Builder::Reset() {
+ input_vertices_.clear();
+ input_edges_.clear();
+ layers_.clear();
+ layer_options_.clear();
+ layer_begins_.clear();
+ layer_is_full_polygon_predicates_.clear();
+ label_set_ids_.clear();
+ label_set_lexicon_.Clear();
+ label_set_.clear();
+ label_set_modified_ = false;
+ sites_.clear();
+ edge_sites_.clear();
+ snapping_needed_ = false;
+}
+
+void S2Builder::ChooseSites() {
+ if (input_vertices_.empty()) return;
+
+ MutableS2ShapeIndex input_edge_index;
+ input_edge_index.Add(make_unique<VertexIdEdgeVectorShape>(
+ input_edges_, input_vertices_));
+ if (options_.split_crossing_edges()) {
+ AddEdgeCrossings(input_edge_index);
+ }
+ if (snapping_requested_) {
+ S2PointIndex<SiteId> site_index;
+ AddForcedSites(&site_index);
+ ChooseInitialSites(&site_index);
+ CollectSiteEdges(site_index);
+ }
+ if (snapping_needed_) {
+ AddExtraSites(input_edge_index);
+ } else {
+ CopyInputEdges();
+ }
+}
+
+void S2Builder::CopyInputEdges() {
+ // Sort the input vertices, discard duplicates, and update the input edges
+ // to refer to the pruned vertex list. (We sort in the same order used by
+ // ChooseInitialSites() to avoid inconsistencies in tests.)
+ vector<InputVertexKey> sorted = SortInputVertices();
+ vector<InputVertexId> vmap(input_vertices_.size());
+ sites_.clear();
+ sites_.reserve(input_vertices_.size());
+ for (int in = 0; in < sorted.size(); ) {
+ const S2Point& site = input_vertices_[sorted[in].second];
+ vmap[sorted[in].second] = sites_.size();
+ while (++in < sorted.size() && input_vertices_[sorted[in].second] == site) {
+ vmap[sorted[in].second] = sites_.size();
+ }
+ sites_.push_back(site);
+ }
+ input_vertices_ = sites_;
+ for (InputEdge& e : input_edges_) {
+ e.first = vmap[e.first];
+ e.second = vmap[e.second];
+ }
+}
+
+vector<S2Builder::InputVertexKey> S2Builder::SortInputVertices() {
+ // Sort all the input vertices in the order that we wish to consider them as
+ // candidate Voronoi sites. Any sort order will produce correct output, so
+ // we have complete flexibility in choosing the sort key. We could even
+ // leave them unsorted, although this would have the disadvantage that
+ // changing the order of the input edges could cause S2Builder to snap to a
+ // different set of Voronoi sites.
+ //
+ // We have chosen to sort them primarily by S2CellId since this improves the
+ // performance of many S2Builder phases (due to better spatial locality).
+ // It also allows the possibility of replacing the current S2PointIndex
+ // approach with a more efficient recursive divide-and-conquer algorithm.
+ //
+ // However, sorting by leaf S2CellId alone has two small disadvantages in
+ // the case where the candidate sites are densely spaced relative to the
+ // snap radius (e.g., when using the IdentitySnapFunction, or when snapping
+ // to E6/E7 near the poles, or snapping to S2CellId/E6/E7 using a snap
+ // radius larger than the minimum value required):
+ //
+ // - First, it tends to bias the Voronoi site locations towards points that
+ // are earlier on the S2CellId Hilbert curve. For example, suppose that
+ // there are two parallel rows of input vertices on opposite sides of the
+ // edge between two large S2Cells, and the rows are separated by less
+ // than the snap radius. Then only vertices from the cell with the
+ // smaller S2CellId are selected, because they are considered first and
+ // prevent us from selecting the sites from the other cell (because they
+ // are closer than "snap_radius" to an existing site).
+ //
+ // - Second, it tends to choose more Voronoi sites than necessary, because
+ // at each step we choose the first site along the Hilbert curve that is
+ // at least "snap_radius" away from all previously selected sites. This
+ // tends to yield sites whose "coverage discs" overlap quite a bit,
+ // whereas it would be better to cover all the input vertices with a
+ // smaller set of coverage discs that don't overlap as much. (This is
+ // the "geometric set cover problem", which is NP-hard.)
+ //
+ // It is not worth going to much trouble to fix these problems, because they
+ // really aren't that important (and don't affect the guarantees made by the
+ // algorithm), but here are a couple of heuristics that might help:
+ //
+ // 1. Sort the input vertices by S2CellId at a coarse level (down to cells
+ // that are O(snap_radius) in size), and then sort by a fingerprint of the
+ // S2Point coordinates (i.e., quasi-randomly). This would retain most of
+ // the advantages of S2CellId sorting, but makes it more likely that we will
+ // select sites that are further apart.
+ //
+ // 2. Rather than choosing the first uncovered input vertex and snapping it
+ // to obtain the next Voronoi site, instead look ahead through following
+ // candidates in S2CellId order and choose the furthest candidate whose
+ // snapped location covers all previous uncovered input vertices.
+ //
+ // TODO(ericv): Experiment with these approaches.
+
+ vector<InputVertexKey> keys;
+ keys.reserve(input_vertices_.size());
+ for (InputVertexId i = 0; i < input_vertices_.size(); ++i) {
+ keys.push_back(InputVertexKey(S2CellId(input_vertices_[i]), i));
+ }
+ std::sort(keys.begin(), keys.end(),
+ [this](const InputVertexKey& a, const InputVertexKey& b) {
+ if (a.first < b.first) return true;
+ if (b.first < a.first) return false;
+ return input_vertices_[a.second] < input_vertices_[b.second];
+ });
+ return keys;
+}
+
+// Check all edge pairs for crossings, and add the corresponding intersection
+// points to input_vertices_. (The intersection points will be snapped and
+// merged with the other vertices during site selection.)
+void S2Builder::AddEdgeCrossings(const MutableS2ShapeIndex& input_edge_index) {
+ // We need to build a list of intersections and add them afterwards so that
+ // we don't reallocate vertices_ during the VisitCrossings() call.
+ vector<S2Point> new_vertices;
+ s2shapeutil::VisitCrossingEdgePairs(
+ input_edge_index, s2shapeutil::CrossingType::INTERIOR,
+ [&new_vertices](const s2shapeutil::ShapeEdge& a,
+ const s2shapeutil::ShapeEdge& b, bool) {
+ new_vertices.push_back(
+ S2::GetIntersection(a.v0(), a.v1(), b.v0(), b.v1()));
+ return true; // Continue visiting.
+ });
+ if (!new_vertices.empty()) {
+ snapping_needed_ = true;
+ for (const auto& vertex : new_vertices) AddVertex(vertex);
+ }
+}
+
+void S2Builder::AddForcedSites(S2PointIndex<SiteId>* site_index) {
+ // Sort the forced sites and remove duplicates.
+ std::sort(sites_.begin(), sites_.end());
+ sites_.erase(std::unique(sites_.begin(), sites_.end()), sites_.end());
+ // Add the forced sites to the index.
+ for (SiteId id = 0; id < sites_.size(); ++id) {
+ site_index->Add(sites_[id], id);
+ }
+ num_forced_sites_ = sites_.size();
+}
+
+void S2Builder::ChooseInitialSites(S2PointIndex<SiteId>* site_index) {
+ // Find all points whose distance is <= min_site_separation_ca_.
+ S2ClosestPointQueryOptions options;
+ options.set_conservative_max_distance(min_site_separation_ca_);
+ S2ClosestPointQuery<SiteId> site_query(site_index, options);
+ vector<S2ClosestPointQuery<SiteId>::Result> results;
+
+ // Apply the snap_function() to each input vertex, then check whether any
+ // existing site is closer than min_vertex_separation(). If not, then add a
+ // new site.
+ //
+ // NOTE(ericv): There are actually two reasonable algorithms, which we call
+ // "snap first" (the one above) and "snap last". The latter checks for each
+ // input vertex whether any existing site is closer than snap_radius(), and
+ // only then applies the snap_function() and adds a new site. "Snap last"
+ // can yield slightly fewer sites in some cases, but it is also more
+ // expensive and can produce surprising results. For example, if you snap
+ // the polyline "0:0, 0:0.7" using IntLatLngSnapFunction(0), the result is
+ // "0:0, 0:0" rather than the expected "0:0, 0:1", because the snap radius
+ // is approximately sqrt(2) degrees and therefore it is legal to snap both
+ // input points to "0:0". "Snap first" produces "0:0, 0:1" as expected.
+ for (const InputVertexKey& key : SortInputVertices()) {
+ const S2Point& vertex = input_vertices_[key.second];
+ S2Point site = SnapSite(vertex);
+ // If any vertex moves when snapped, the output cannot be idempotent.
+ snapping_needed_ = snapping_needed_ || site != vertex;
+
+ // FindClosestPoints() measures distances conservatively, so we need to
+ // recheck the distances using exact predicates.
+ //
+ // NOTE(ericv): When the snap radius is large compared to the average
+ // vertex spacing, we could possibly avoid the call the FindClosestPoints
+ // by checking whether sites_.back() is close enough.
+ S2ClosestPointQueryPointTarget target(site);
+ site_query.FindClosestPoints(&target, &results);
+ bool add_site = true;
+ for (const auto& result : results) {
+ if (s2pred::CompareDistance(site, result.point(),
+ min_site_separation_ca_) <= 0) {
+ add_site = false;
+ // This pair of sites is too close. If the sites are distinct, then
+ // the output cannot be idempotent.
+ snapping_needed_ = snapping_needed_ || site != result.point();
+ }
+ }
+ if (add_site) {
+ site_index->Add(site, sites_.size());
+ sites_.push_back(site);
+ site_query.ReInit();
+ }
+ }
+}
+
+S2Point S2Builder::SnapSite(const S2Point& point) const {
+ if (!snapping_requested_) return point;
+ S2Point site = options_.snap_function().SnapPoint(point);
+S1ChordAngle dist_moved(site, point);
+ if (dist_moved > site_snap_radius_ca_) {
+ error_->Init(S2Error::BUILDER_SNAP_RADIUS_TOO_SMALL,
+ "Snap function moved vertex (%.15g, %.15g, %.15g) "
+ "by %.15g, which is more than the specified snap "
+ "radius of %.15g", point.x(), point.y(), point.z(),
+ dist_moved.ToAngle().radians(),
+ site_snap_radius_ca_.ToAngle().radians());
+ }
+ return site;
+}
+
+// For each edge, find all sites within min_edge_site_query_radius_ca_ and
+// store them in edge_sites_. Also, to implement idempotency this method also
+// checks whether the input vertices and edges may already satisfy the output
+// criteria. If any problems are found then snapping_needed_ is set to true.
+void S2Builder::CollectSiteEdges(const S2PointIndex<SiteId>& site_index) {
+ // Find all points whose distance is <= edge_site_query_radius_ca_.
+ S2ClosestPointQueryOptions options;
+ options.set_conservative_max_distance(edge_site_query_radius_ca_);
+ S2ClosestPointQuery<SiteId> site_query(&site_index, options);
+ vector<S2ClosestPointQuery<SiteId>::Result> results;
+ edge_sites_.resize(input_edges_.size());
+ for (InputEdgeId e = 0; e < input_edges_.size(); ++e) {
+ const InputEdge& edge = input_edges_[e];
+ const S2Point& v0 = input_vertices_[edge.first];
+ const S2Point& v1 = input_vertices_[edge.second];
+ if (s2builder_verbose) {
+ cpp_compat_cout << "S2Polyline: " << s2textformat::ToString(v0)
+ << ", " << s2textformat::ToString(v1) << "\n";
+ }
+ S2ClosestPointQueryEdgeTarget target(v0, v1);
+ site_query.FindClosestPoints(&target, &results);
+ auto* sites = &edge_sites_[e];
+ sites->reserve(results.size());
+ for (const auto& result : results) {
+ sites->push_back(result.data());
+ if (!snapping_needed_ &&
+ result.distance() < min_edge_site_separation_ca_limit_ &&
+ result.point() != v0 && result.point() != v1 &&
+ s2pred::CompareEdgeDistance(result.point(), v0, v1,
+ min_edge_site_separation_ca_) < 0) {
+ snapping_needed_ = true;
+ }
+ }
+ SortSitesByDistance(v0, sites);
+ }
+}
+
+void S2Builder::SortSitesByDistance(const S2Point& x,
+ compact_array<SiteId>* sites) const {
+ // Sort sites in increasing order of distance to X.
+ std::sort(sites->begin(), sites->end(),
+ [&x, this](SiteId i, SiteId j) {
+ return s2pred::CompareDistances(x, sites_[i], sites_[j]) < 0;
+ });
+}
+
+// There are two situatons where we need to add extra Voronoi sites in order
+// to ensure that the snapped edges meet the output requirements:
+//
+// (1) If a snapped edge deviates from its input edge by more than
+// max_edge_deviation(), we add a new site on the input edge near the
+// middle of the snapped edge. This causes the snapped edge to split
+// into two pieces, so that it follows the input edge more closely.
+//
+// (2) If a snapped edge is closer than min_edge_vertex_separation() to any
+// nearby site (the "site to avoid"), then we add a new site (the
+// "separation site") on the input edge near the site to avoid. This
+// causes the snapped edge to follow the input edge more closely and is
+// guaranteed to increase the separation to the required distance.
+//
+// We check these conditions by snapping all the input edges to a chain of
+// Voronoi sites and then testing each edge in the chain. If a site needs to
+// be added, we mark all nearby edges for re-snapping.
+void S2Builder::AddExtraSites(const MutableS2ShapeIndex& input_edge_index) {
+ // When options_.split_crossing_edges() is true, this function may be called
+ // even when site_snap_radius_ca_ == 0 (because edge_snap_radius_ca_ > 0).
+ // However neither of the conditions above apply in that case.
+ if (site_snap_radius_ca_ == S1ChordAngle::Zero()) return;
+
+ vector<SiteId> chain; // Temporary
+ vector<InputEdgeId> snap_queue;
+ for (InputEdgeId max_e = 0; max_e < input_edges_.size(); ++max_e) {
+ snap_queue.push_back(max_e);
+ while (!snap_queue.empty()) {
+ InputEdgeId e = snap_queue.back();
+ snap_queue.pop_back();
+ SnapEdge(e, &chain);
+ // We could save the snapped chain here in a snapped_chains_ vector, to
+ // avoid resnapping it in AddSnappedEdges() below, however currently
+ // SnapEdge only accounts for less than 5% of the runtime.
+ MaybeAddExtraSites(e, max_e, chain, input_edge_index, &snap_queue);
+ }
+ }
+}
+
+void S2Builder::MaybeAddExtraSites(InputEdgeId edge_id,
+ InputEdgeId max_edge_id,
+ const vector<SiteId>& chain,
+ const MutableS2ShapeIndex& input_edge_index,
+ vector<InputEdgeId>* snap_queue) {
+ // The snapped chain is always a *subsequence* of the nearby sites
+ // (edge_sites_), so we walk through the two arrays in parallel looking for
+ // sites that weren't snapped. We also keep track of the current snapped
+ // edge, since it is the only edge that can be too close.
+ int i = 0;
+ for (SiteId id : edge_sites_[edge_id]) {
+ if (id == chain[i]) {
+ if (++i == chain.size()) break;
+ // Check whether this snapped edge deviates too far from its original
+ // position. If so, we split the edge by adding an extra site.
+ const S2Point& v0 = sites_[chain[i - 1]];
+ const S2Point& v1 = sites_[chain[i]];
+ if (S1ChordAngle(v0, v1) < min_edge_length_to_split_ca_) continue;
+
+ const InputEdge& edge = input_edges_[edge_id];
+ const S2Point& a0 = input_vertices_[edge.first];
+ const S2Point& a1 = input_vertices_[edge.second];
+ if (!S2::IsEdgeBNearEdgeA(a0, a1, v0, v1, max_edge_deviation_)) {
+ // Add a new site on the input edge, positioned so that it splits the
+ // snapped edge into two approximately equal pieces. Then we find all
+ // the edges near the new site (including this one) and add them to
+ // the snap queue.
+ //
+ // Note that with large snap radii, it is possible that the snapped
+ // edge wraps around the sphere the "wrong way". To handle this we
+ // find the preferred split location by projecting both endpoints onto
+ // the input edge and taking their midpoint.
+ S2Point mid = (S2::Project(v0, a0, a1) +
+ S2::Project(v1, a0, a1)).Normalize();
+ S2Point new_site = GetSeparationSite(mid, v0, v1, edge_id);
+ AddExtraSite(new_site, max_edge_id, input_edge_index, snap_queue);
+ return;
+ }
+ } else if (i > 0 && id >= num_forced_sites_) {
+ // Check whether this "site to avoid" is closer to the snapped edge than
+ // min_edge_vertex_separation(). Note that this is the only edge of the
+ // chain that can be too close because its vertices must span the point
+ // where "site_to_avoid" projects onto the input edge XY (this claim
+ // relies on the fact that all sites are separated by at least the snap
+ // radius). We don't try to avoid sites added using ForceVertex()
+ // because we don't guarantee any minimum separation from such sites.
+ const S2Point& site_to_avoid = sites_[id];
+ const S2Point& v0 = sites_[chain[i - 1]];
+ const S2Point& v1 = sites_[chain[i]];
+ if (s2pred::CompareEdgeDistance(
+ site_to_avoid, v0, v1, min_edge_site_separation_ca_) < 0) {
+ // A snapped edge can only approach a site too closely when there are
+ // no sites near the input edge near that point. We fix that by
+ // adding a new site along the input edge (a "separation site"), then
+ // we find all the edges near the new site (including this one) and
+ // add them to the snap queue.
+ S2Point new_site = GetSeparationSite(site_to_avoid, v0, v1, edge_id);
+ S2_DCHECK_NE(site_to_avoid, new_site);
+ AddExtraSite(new_site, max_edge_id, input_edge_index, snap_queue);
+ return;
+ }
+ }
+ }
+}
+
+// Adds a new site, then updates "edge_sites"_ for all edges near the new site
+// and adds them to "snap_queue" for resnapping (unless their edge id exceeds
+// "max_edge_id", since those edges have not been snapped the first time yet).
+void S2Builder::AddExtraSite(const S2Point& new_site,
+ InputEdgeId max_edge_id,
+ const MutableS2ShapeIndex& input_edge_index,
+ vector<InputEdgeId>* snap_queue) {
+ SiteId new_site_id = sites_.size();
+ sites_.push_back(new_site);
+ // Find all edges whose distance is <= edge_site_query_radius_ca_.
+ S2ClosestEdgeQuery::Options options;
+ options.set_conservative_max_distance(edge_site_query_radius_ca_);
+ options.set_include_interiors(false);
+ S2ClosestEdgeQuery query(&input_edge_index, options);
+ S2ClosestEdgeQuery::PointTarget target(new_site);
+ for (const auto& result : query.FindClosestEdges(&target)) {
+ InputEdgeId e = result.edge_id();
+ auto* site_ids = &edge_sites_[e];
+ site_ids->push_back(new_site_id);
+ SortSitesByDistance(input_vertices_[input_edges_[e].first], site_ids);
+ if (e <= max_edge_id) snap_queue->push_back(e);
+ }
+}
+
+S2Point S2Builder::GetSeparationSite(const S2Point& site_to_avoid,
+ const S2Point& v0, const S2Point& v1,
+ InputEdgeId input_edge_id) const {
+ // Define the "coverage disc" of a site S to be the disc centered at S with
+ // radius "snap_radius". Similarly, define the "coverage interval" of S for
+ // an edge XY to be the intersection of XY with the coverage disc of S. The
+ // SnapFunction implementations guarantee that the only way that a snapped
+ // edge can be closer than min_edge_vertex_separation() to a non-snapped
+ // site (i.e., site_to_avoid) if is there is a gap in the coverage of XY
+ // near this site. We can fix this problem simply by adding a new site to
+ // fill this gap, located as closely as possible to the site to avoid.
+ //
+ // To calculate the coverage gap, we look at the two snapped sites on
+ // either side of site_to_avoid, and find the endpoints of their coverage
+ // intervals. The we place a new site in the gap, located as closely as
+ // possible to the site to avoid. Note that the new site may move when it
+ // is snapped by the snap_function, but it is guaranteed not to move by
+ // more than snap_radius and therefore its coverage interval will still
+ // intersect the gap.
+ const InputEdge& edge = input_edges_[input_edge_id];
+ const S2Point& x = input_vertices_[edge.first];
+ const S2Point& y = input_vertices_[edge.second];
+ Vector3_d xy_dir = y - x;
+ S2Point n = S2::RobustCrossProd(x, y);
+ S2Point new_site = S2::Project(site_to_avoid, x, y, n);
+ S2Point gap_min = GetCoverageEndpoint(v0, x, y, n);
+ S2Point gap_max = GetCoverageEndpoint(v1, y, x, -n);
+ if ((new_site - gap_min).DotProd(xy_dir) < 0) {
+ new_site = gap_min;
+ } else if ((gap_max - new_site).DotProd(xy_dir) < 0) {
+ new_site = gap_max;
+ }
+ new_site = SnapSite(new_site);
+ S2_DCHECK_NE(v0, new_site);
+ S2_DCHECK_NE(v1, new_site);
+ return new_site;
+}
+
+// Given a site P and an edge XY with normal N, intersect XY with the disc of
+// radius snap_radius() around P, and return the intersection point that is
+// further along the edge XY toward Y.
+S2Point S2Builder::GetCoverageEndpoint(const S2Point& p, const S2Point& x,
+ const S2Point& y, const S2Point& n)
+ const {
+ // Consider the plane perpendicular to P that cuts off a spherical cap of
+ // radius snap_radius(). This plane intersects the plane through the edge
+ // XY (perpendicular to N) along a line, and that line intersects the unit
+ // sphere at two points Q and R, and we want to return the point R that is
+ // further along the edge XY toward Y.
+ //
+ // Let M be the midpoint of QR. This is the point along QR that is closest
+ // to P. We can now express R as the sum of two perpendicular vectors OM
+ // and MR in the plane XY. Vector MR is in the direction N x P, while
+ // vector OM is in the direction (N x P) x N, where N = X x Y.
+ //
+ // The length of OM can be found using the Pythagorean theorem on triangle
+ // OPM, and the length of MR can be found using the Pythagorean theorem on
+ // triangle OMR.
+ //
+ // In the calculations below, we save some work by scaling all the vectors
+ // by n.CrossProd(p).Norm2(), and normalizing at the end.
+ double n2 = n.Norm2();
+ double nDp = n.DotProd(p);
+ S2Point nXp = n.CrossProd(p);
+ S2Point nXpXn = n2 * p - nDp * n;
+ Vector3_d om = sqrt(1 - edge_snap_radius_sin2_) * nXpXn;
+ double mr2 = edge_snap_radius_sin2_ * n2 - nDp * nDp;
+
+ // MR is constructed so that it points toward Y (rather than X).
+ Vector3_d mr = sqrt(max(0.0, mr2)) * nXp;
+ return (om + mr).Normalize();
+}
+
+void S2Builder::SnapEdge(InputEdgeId e, vector<SiteId>* chain) const {
+ chain->clear();
+ const InputEdge& edge = input_edges_[e];
+ if (!snapping_needed_) {
+ chain->push_back(edge.first);
+ chain->push_back(edge.second);
+ return;
+ }
+
+ const S2Point& x = input_vertices_[edge.first];
+ const S2Point& y = input_vertices_[edge.second];
+
+ // Optimization: if there is only one nearby site, return.
+ // Optimization: if there are exactly two nearby sites, and one is close
+ // enough to each vertex, then return.
+
+ // Now iterate through the sites. We keep track of the sequence of sites
+ // that are visited.
+ const auto& candidates = edge_sites_[e];
+ for (SiteId site_id : candidates) {
+ const S2Point& c = sites_[site_id];
+ // Skip any sites that are too far away. (There will be some of these,
+ // because we also keep track of "sites to avoid".) Note that some sites
+ // may be close enough to the line containing the edge, but not to the
+ // edge itself, so we can just use the dot product with the edge normal.
+ if (s2pred::CompareEdgeDistance(c, x, y, edge_snap_radius_ca_) > 0) {
+ continue;
+ }
+ // Check whether the new site C excludes the previous site B. If so,
+ // repeat with the previous site, and so on.
+ bool add_site_c = true;
+ for (; !chain->empty(); chain->pop_back()) {
+ S2Point b = sites_[chain->back()];
+
+ // First, check whether B and C are so far apart that their clipped
+ // Voronoi regions can't intersect.
+ S1ChordAngle bc(b, c);
+ if (bc >= max_adjacent_site_separation_ca_) break;
+
+ // Otherwise, we want to check whether site C prevents the Voronoi
+ // region of B from intersecting XY, or vice versa. This can be
+ // determined by computing the "coverage interval" (the segment of XY
+ // intersected by the coverage disc of radius snap_radius) for each
+ // site. If the coverage interval of one site contains the coverage
+ // interval of the other, then the contained site can be excluded.
+ s2pred::Excluded result = s2pred::GetVoronoiSiteExclusion(
+ b, c, x, y, edge_snap_radius_ca_);
+ if (result == s2pred::Excluded::FIRST) continue; // Site B excluded by C
+ if (result == s2pred::Excluded::SECOND) {
+ add_site_c = false; // Site C is excluded by B.
+ break;
+ }
+ S2_DCHECK_EQ(s2pred::Excluded::NEITHER, result);
+
+ // Otherwise check whether the previous site A is close enough to B and
+ // C that it might further clip the Voronoi region of B.
+ if (chain->size() < 2) break;
+ S2Point a = sites_[chain->end()[-2]];
+ S1ChordAngle ac(a, c);
+ if (ac >= max_adjacent_site_separation_ca_) break;
+
+ // If triangles ABC and XYB have the same orientation, the circumcenter
+ // Z of ABC is guaranteed to be on the same side of XY as B.
+ int xyb = s2pred::Sign(x, y, b);
+ if (s2pred::Sign(a, b, c) == xyb) {
+ break; // The circumcenter is on the same side as B but further away.
+ }
+ // Other possible optimizations:
+ // - if AB > max_adjacent_site_separation_ca_ then keep B.
+ // - if d(B, XY) < 0.5 * min(AB, BC) then keep B.
+
+ // If the circumcenter of ABC is on the same side of XY as B, then B is
+ // excluded by A and C combined. Otherwise B is needed and we can exit.
+ if (s2pred::EdgeCircumcenterSign(x, y, a, b, c) != xyb) break;
+ }
+ if (add_site_c) {
+ chain->push_back(site_id);
+ }
+ }
+ S2_DCHECK(!chain->empty());
+ if (google::DEBUG_MODE) {
+ for (SiteId site_id : candidates) {
+ if (s2pred::CompareDistances(y, sites_[chain->back()],
+ sites_[site_id]) > 0) {
+ S2_LOG(ERROR) << "Snapping invariant broken!";
+ }
+ }
+ }
+ if (s2builder_verbose) {
+ cpp_compat_cout << "(" << edge.first << "," << edge.second << "): ";
+ for (SiteId id : *chain) cpp_compat_cout << id << " ";
+ cpp_compat_cout << std::endl;
+ }
+}
+
+void S2Builder::BuildLayers() {
+ // Each output edge has an "input edge id set id" (an int32) representing
+ // the set of input edge ids that were snapped to this edge. The actual
+ // InputEdgeIds can be retrieved using "input_edge_id_set_lexicon".
+ vector<vector<Edge>> layer_edges;
+ vector<vector<InputEdgeIdSetId>> layer_input_edge_ids;
+ IdSetLexicon input_edge_id_set_lexicon;
+ BuildLayerEdges(&layer_edges, &layer_input_edge_ids,
+ &input_edge_id_set_lexicon);
+
+ // At this point we have no further need for the input geometry or nearby
+ // site data, so we clear those fields to save space.
+ vector<S2Point>().swap(input_vertices_);
+ vector<InputEdge>().swap(input_edges_);
+ vector<compact_array<SiteId>>().swap(edge_sites_);
+
+ // If there are a large number of layers, then we build a minimal subset of
+ // vertices for each layer. This ensures that layer types that iterate over
+ // vertices will run in time proportional to the size of that layer rather
+ // than the size of all layers combined.
+ vector<vector<S2Point>> layer_vertices;
+ static const int kMinLayersForVertexFiltering = 10;
+ if (layers_.size() >= kMinLayersForVertexFiltering) {
+ // Disable vertex filtering if it is disallowed by any layer. (This could
+ // be optimized, but in current applications either all layers allow
+ // filtering or none of them do.)
+ bool allow_vertex_filtering = false;
+ for (const auto& options : layer_options_) {
+ allow_vertex_filtering &= options.allow_vertex_filtering();
+ }
+ if (allow_vertex_filtering) {
+ vector<Graph::VertexId> filter_tmp; // Temporary used by FilterVertices.
+ layer_vertices.resize(layers_.size());
+ for (int i = 0; i < layers_.size(); ++i) {
+ layer_vertices[i] = Graph::FilterVertices(sites_, &layer_edges[i],
+ &filter_tmp);
+ }
+ vector<S2Point>().swap(sites_); // Release memory
+ }
+ }
+ for (int i = 0; i < layers_.size(); ++i) {
+ const vector<S2Point>& vertices = (layer_vertices.empty() ?
+ sites_ : layer_vertices[i]);
+ Graph graph(layer_options_[i], &vertices, &layer_edges[i],
+ &layer_input_edge_ids[i], &input_edge_id_set_lexicon,
+ &label_set_ids_, &label_set_lexicon_,
+ layer_is_full_polygon_predicates_[i]);
+ layers_[i]->Build(graph, error_);
+ // Don't free the layer data until all layers have been built, in order to
+ // support building multiple layers at once (e.g. ClosedSetNormalizer).
+ }
+}
+
+static void DumpEdges(const vector<S2Builder::Graph::Edge>& edges,
+ const vector<S2Point>& vertices) {
+ for (const auto& e : edges) {
+ vector<S2Point> v;
+ v.push_back(vertices[e.first]);
+ v.push_back(vertices[e.second]);
+ cpp_compat_cout << "S2Polyline: " << s2textformat::ToString(v)
+ << "(" << e.first << "," << e.second << ")" << std::endl;
+ }
+}
+
+// Snaps and possibly simplifies the edges for each layer, populating the
+// given output arguments. The resulting edges can be used to construct an
+// S2Builder::Graph directly (no further processing is necessary).
+//
+// This method is not "const" because Graph::ProcessEdges can modify
+// layer_options_ in some cases (changing undirected edges to directed ones).
+void S2Builder::BuildLayerEdges(
+ vector<vector<Edge>>* layer_edges,
+ vector<vector<InputEdgeIdSetId>>* layer_input_edge_ids,
+ IdSetLexicon* input_edge_id_set_lexicon) {
+ // Edge chains are simplified only when a non-zero snap radius is specified.
+ // If so, we build a map from each site to the set of input vertices that
+ // snapped to that site.
+ vector<compact_array<InputVertexId>> site_vertices;
+ bool simplify = snapping_needed_ && options_.simplify_edge_chains();
+ if (simplify) site_vertices.resize(sites_.size());
+
+ layer_edges->resize(layers_.size());
+ layer_input_edge_ids->resize(layers_.size());
+ for (int i = 0; i < layers_.size(); ++i) {
+ AddSnappedEdges(layer_begins_[i], layer_begins_[i+1], layer_options_[i],
+ &(*layer_edges)[i], &(*layer_input_edge_ids)[i],
+ input_edge_id_set_lexicon, &site_vertices);
+ }
+ if (simplify) {
+ SimplifyEdgeChains(site_vertices, layer_edges, layer_input_edge_ids,
+ input_edge_id_set_lexicon);
+ }
+ // We simplify edge chains before processing the per-layer GraphOptions
+ // because simplification can create duplicate edges and/or sibling edge
+ // pairs which may need to be removed.
+ for (int i = 0; i < layers_.size(); ++i) {
+ // The errors generated by ProcessEdges are really warnings, so we simply
+ // record them and continue.
+ Graph::ProcessEdges(&layer_options_[i], &(*layer_edges)[i],
+ &(*layer_input_edge_ids)[i],
+ input_edge_id_set_lexicon, error_);
+ }
+}
+
+// Snaps all the input edges for a given layer, populating the given output
+// arguments. If (*site_vertices) is non-empty then it is updated so that
+// (*site_vertices)[site] contains a list of all input vertices that were
+// snapped to that site.
+void S2Builder::AddSnappedEdges(
+ InputEdgeId begin, InputEdgeId end, const GraphOptions& options,
+ vector<Edge>* edges, vector<InputEdgeIdSetId>* input_edge_ids,
+ IdSetLexicon* input_edge_id_set_lexicon,
+ vector<compact_array<InputVertexId>>* site_vertices) const {
+ bool discard_degenerate_edges = (options.degenerate_edges() ==
+ GraphOptions::DegenerateEdges::DISCARD);
+ vector<SiteId> chain;
+ for (InputEdgeId e = begin; e < end; ++e) {
+ InputEdgeIdSetId id = input_edge_id_set_lexicon->AddSingleton(e);
+ SnapEdge(e, &chain);
+ MaybeAddInputVertex(input_edges_[e].first, chain[0], site_vertices);
+ if (chain.size() == 1) {
+ if (discard_degenerate_edges) continue;
+ AddSnappedEdge(chain[0], chain[0], id, options.edge_type(),
+ edges, input_edge_ids);
+ } else {
+ MaybeAddInputVertex(input_edges_[e].second, chain.back(), site_vertices);
+ for (int i = 1; i < chain.size(); ++i) {
+ AddSnappedEdge(chain[i-1], chain[i], id, options.edge_type(),
+ edges, input_edge_ids);
+ }
+ }
+ }
+ if (s2builder_verbose) DumpEdges(*edges, sites_);
+}
+
+// If "site_vertices" is non-empty, ensures that (*site_vertices)[id] contains
+// "v". Duplicate entries are allowed.
+inline void S2Builder::MaybeAddInputVertex(
+ InputVertexId v, SiteId id,
+ vector<compact_array<InputVertexId>>* site_vertices) const {
+ if (site_vertices->empty()) return;
+
+ // Optimization: check if we just added this vertex. This is worthwhile
+ // because the input edges usually form a continuous chain, i.e. the
+ // destination of one edge is the same as the source of the next edge.
+ auto& vertices = (*site_vertices)[id];
+ if (vertices.empty() || vertices.back() != v) {
+ vertices.push_back(v);
+ }
+}
+
+// Adds the given edge to "edges" and "input_edge_ids". If undirected edges
+// are being used, also adds an edge in the opposite direction.
+inline void S2Builder::AddSnappedEdge(
+ SiteId src, SiteId dst, InputEdgeIdSetId id, EdgeType edge_type,
+ vector<Edge>* edges, vector<InputEdgeIdSetId>* input_edge_ids) const {
+ edges->push_back(Edge(src, dst));
+ input_edge_ids->push_back(id);
+ if (edge_type == EdgeType::UNDIRECTED) {
+ edges->push_back(Edge(dst, src));
+ // Automatically created edges do not have input edge ids or labels. This
+ // can be used to distinguish the original direction of the undirected edge.
+ input_edge_ids->push_back(IdSetLexicon::EmptySetId());
+ }
+}
+
+// A class that encapsulates the state needed for simplifying edge chains.
+class S2Builder::EdgeChainSimplifier {
+ public:
+ // The graph "g" contains all edges from all layers. "edge_layers"
+ // indicates the original layer for each edge. "site_vertices" is a map
+ // from SiteId to the set of InputVertexIds that were snapped to that site.
+ // "layer_edges" and "layer_input_edge_ids" are output arguments where the
+ // simplified edge chains will be placed. The input and output edges are
+ // not sorted.
+ EdgeChainSimplifier(
+ const S2Builder& builder, const Graph& g,
+ const vector<int>& edge_layers,
+ const vector<compact_array<InputVertexId>>& site_vertices,
+ vector<vector<Edge>>* layer_edges,
+ vector<vector<InputEdgeIdSetId>>* layer_input_edge_ids,
+ IdSetLexicon* input_edge_id_set_lexicon);
+
+ void Run();
+
+ private:
+ using VertexId = Graph::VertexId;
+
+ class InteriorVertexMatcher;
+ void OutputEdge(EdgeId e);
+ int graph_edge_layer(EdgeId e) const;
+ int input_edge_layer(InputEdgeId id) const;
+ bool IsInterior(VertexId v);
+ void SimplifyChain(VertexId v0, VertexId v1);
+ Graph::VertexId FollowChain(VertexId v0, VertexId v1) const;
+ void OutputAllEdges(VertexId v0, VertexId v1);
+ bool TargetInputVertices(VertexId v, S2PolylineSimplifier* simplifier) const;
+ bool AvoidSites(VertexId v0, VertexId v1, VertexId v2,
+ S2PolylineSimplifier* simplifier) const;
+ void MergeChain(const vector<VertexId>& vertices);
+ void AssignDegenerateEdges(
+ const vector<InputEdgeId>& degenerate_ids,
+ vector<vector<InputEdgeId>>* merged_input_ids) const;
+
+ const S2Builder& builder_;
+ const Graph& g_;
+ Graph::VertexInMap in_;
+ Graph::VertexOutMap out_;
+ vector<int> edge_layers_;
+ const vector<compact_array<InputVertexId>>& site_vertices_;
+ vector<vector<Edge>>* layer_edges_;
+ vector<vector<InputEdgeIdSetId>>* layer_input_edge_ids_;
+ IdSetLexicon* input_edge_id_set_lexicon_;
+
+ // Convenience member copied from builder_.
+ const std::vector<InputEdgeId>& layer_begins_;
+
+ // is_interior_[v] indicates that VertexId "v" is eligible to be an interior
+ // vertex of a simplified edge chain. You can think of it as vertex whose
+ // indegree and outdegree are both 1 (although the actual definition is a
+ // bit more complicated because of duplicate edges and layers).
+ vector<bool> is_interior_;
+
+ // used_[e] indicates that EdgeId "e" has already been processed.
+ vector<bool> used_;
+
+ // Temporary vectors, declared here to avoid repeated allocation.
+ vector<VertexId> tmp_vertices_;
+ vector<EdgeId> tmp_edges_;
+
+ // The output edges after simplification.
+ vector<Edge> new_edges_;
+ vector<InputEdgeIdSetId> new_input_edge_ids_;
+ vector<int> new_edge_layers_;
+};
+
+// Simplifies edge chains, updating its input/output arguments as necessary.
+void S2Builder::SimplifyEdgeChains(
+ const vector<compact_array<InputVertexId>>& site_vertices,
+ vector<vector<Edge>>* layer_edges,
+ vector<vector<InputEdgeIdSetId>>* layer_input_edge_ids,
+ IdSetLexicon* input_edge_id_set_lexicon) const {
+ if (layers_.empty()) return;
+
+ // Merge the edges from all layers (in order to build a single graph).
+ vector<Edge> merged_edges;
+ vector<InputEdgeIdSetId> merged_input_edge_ids;
+ vector<int> merged_edge_layers;
+ MergeLayerEdges(*layer_edges, *layer_input_edge_ids,
+ &merged_edges, &merged_input_edge_ids, &merged_edge_layers);
+
+ // The following fields will be reconstructed by EdgeChainSimplifier.
+ for (auto& edges : *layer_edges) edges.clear();
+ for (auto& input_edge_ids : *layer_input_edge_ids) input_edge_ids.clear();
+
+ // The graph options are irrelevant for edge chain simplification, but we
+ // try to set them appropriately anyway.
+ S2Builder::GraphOptions graph_options(EdgeType::DIRECTED,
+ GraphOptions::DegenerateEdges::KEEP,
+ GraphOptions::DuplicateEdges::KEEP,
+ GraphOptions::SiblingPairs::KEEP);
+ Graph graph(graph_options, &sites_, &merged_edges, &merged_input_edge_ids,
+ input_edge_id_set_lexicon, nullptr, nullptr,
+ IsFullPolygonPredicate());
+ EdgeChainSimplifier simplifier(
+ *this, graph, merged_edge_layers, site_vertices,
+ layer_edges, layer_input_edge_ids, input_edge_id_set_lexicon);
+ simplifier.Run();
+}
+
+// Merges the edges from all layers and sorts them in lexicographic order so
+// that we can construct a single graph. The sort is stable, which means that
+// any duplicate edges within each layer will still be sorted by InputEdgeId.
+void S2Builder::MergeLayerEdges(
+ const vector<vector<Edge>>& layer_edges,
+ const vector<vector<InputEdgeIdSetId>>& layer_input_edge_ids,
+ vector<Edge>* edges, vector<InputEdgeIdSetId>* input_edge_ids,
+ vector<int>* edge_layers) const {
+ vector<LayerEdgeId> order;
+ for (int i = 0; i < layer_edges.size(); ++i) {
+ for (int e = 0; e < layer_edges[i].size(); ++e) {
+ order.push_back(LayerEdgeId(i, e));
+ }
+ }
+ std::sort(order.begin(), order.end(),
+ [&layer_edges](const LayerEdgeId& ai, const LayerEdgeId& bi) {
+ return StableLessThan(layer_edges[ai.first][ai.second],
+ layer_edges[bi.first][bi.second], ai, bi);
+ });
+ edges->reserve(order.size());
+ input_edge_ids->reserve(order.size());
+ edge_layers->reserve(order.size());
+ for (const LayerEdgeId& id : order) {
+ edges->push_back(layer_edges[id.first][id.second]);
+ input_edge_ids->push_back(layer_input_edge_ids[id.first][id.second]);
+ edge_layers->push_back(id.first);
+ }
+}
+
+// A comparison function that allows stable sorting with std::sort (which is
+// fast but not stable). It breaks ties between equal edges by comparing
+// their LayerEdgeIds.
+inline bool S2Builder::StableLessThan(
+ const Edge& a, const Edge& b,
+ const LayerEdgeId& ai, const LayerEdgeId& bi) {
+ // The compiler doesn't optimize this as well as it should:
+ // return make_pair(a, ai) < make_pair(b, bi);
+ if (a.first < b.first) return true;
+ if (b.first < a.first) return false;
+ if (a.second < b.second) return true;
+ if (b.second < a.second) return false;
+ return ai < bi; // Stable sort.
+}
+
+S2Builder::EdgeChainSimplifier::EdgeChainSimplifier(
+ const S2Builder& builder, const Graph& g, const vector<int>& edge_layers,
+ const vector<compact_array<InputVertexId>>& site_vertices,
+ vector<vector<Edge>>* layer_edges,
+ vector<vector<InputEdgeIdSetId>>* layer_input_edge_ids,
+ IdSetLexicon* input_edge_id_set_lexicon)
+ : builder_(builder), g_(g), in_(g), out_(g), edge_layers_(edge_layers),
+ site_vertices_(site_vertices), layer_edges_(layer_edges),
+ layer_input_edge_ids_(layer_input_edge_ids),
+ input_edge_id_set_lexicon_(input_edge_id_set_lexicon),
+ layer_begins_(builder_.layer_begins_),
+ is_interior_(g.num_vertices()), used_(g.num_edges()) {
+ new_edges_.reserve(g.num_edges());
+ new_input_edge_ids_.reserve(g.num_edges());
+ new_edge_layers_.reserve(g.num_edges());
+}
+
+void S2Builder::EdgeChainSimplifier::Run() {
+ // Determine which vertices can be interior vertices of an edge chain.
+ for (VertexId v = 0; v < g_.num_vertices(); ++v) {
+ is_interior_[v] = IsInterior(v);
+ }
+ // Attempt to simplify all edge chains that start from a non-interior
+ // vertex. (This takes care of all chains except loops.)
+ for (EdgeId e = 0; e < g_.num_edges(); ++e) {
+ if (used_[e]) continue;
+ Edge edge = g_.edge(e);
+ if (is_interior_[edge.first]) continue;
+ if (!is_interior_[edge.second]) {
+ OutputEdge(e); // An edge between two non-interior vertices.
+ } else {
+ SimplifyChain(edge.first, edge.second);
+ }
+ }
+ // If there are any edges left, they form one or more disjoint loops where
+ // all vertices are interior vertices.
+ //
+ // TODO(ericv): It would be better to start from the edge with the smallest
+ // min_input_edge_id(), since that would make the output more predictable
+ // for testing purposes. It also means that we won't create an edge that
+ // spans the start and end of a polyline if the polyline is snapped into a
+ // loop. (Unfortunately there are pathological examples that prevent us
+ // from guaranteeing this in general, e.g. there could be two polylines in
+ // different layers that snap to the same loop but start at different
+ // positions. In general we only consider input edge ids to be a hint
+ // towards the preferred output ordering.)
+ for (EdgeId e = 0; e < g_.num_edges(); ++e) {
+ if (used_[e]) continue;
+ Edge edge = g_.edge(e);
+ if (edge.first == edge.second) {
+ // Note that it is safe to output degenerate edges as we go along,
+ // because this vertex has at least one non-degenerate outgoing edge and
+ // therefore we will (or just did) start an edge chain here.
+ OutputEdge(e);
+ } else {
+ SimplifyChain(edge.first, edge.second);
+ }
+ }
+
+ // Finally, copy the output edges into the appropriate layers. They don't
+ // need to be sorted because the input edges were also unsorted.
+ for (int e = 0; e < new_edges_.size(); ++e) {
+ int layer = new_edge_layers_[e];
+ (*layer_edges_)[layer].push_back(new_edges_[e]);
+ (*layer_input_edge_ids_)[layer].push_back(new_input_edge_ids_[e]);
+ }
+}
+
+// Copies the given edge to the output and marks it as used.
+inline void S2Builder::EdgeChainSimplifier::OutputEdge(EdgeId e) {
+ new_edges_.push_back(g_.edge(e));
+ new_input_edge_ids_.push_back(g_.input_edge_id_set_id(e));
+ new_edge_layers_.push_back(edge_layers_[e]);
+ used_[e] = true;
+}
+
+// Returns the layer that a given graph edge belongs to.
+inline int S2Builder::EdgeChainSimplifier::graph_edge_layer(EdgeId e) const {
+ return edge_layers_[e];
+}
+
+// Returns the layer than a given input edge belongs to.
+int S2Builder::EdgeChainSimplifier::input_edge_layer(InputEdgeId id) const {
+ // NOTE(ericv): If this method shows up in profiling, the result could be
+ // stored with each edge (i.e., edge_layers_ and new_edge_layers_).
+ S2_DCHECK_GE(id, 0);
+ return (std::upper_bound(layer_begins_.begin(), layer_begins_.end(), id) -
+ (layer_begins_.begin() + 1));
+}
+
+// A helper class for determining whether a vertex can be an interior vertex
+// of a simplified edge chain. Such a vertex must be adjacent to exactly two
+// vertices (across all layers combined), and in each layer the number of
+// incoming edges from one vertex must equal the number of outgoing edges to
+// the other vertex (in both directions). Furthermore the vertex cannot have
+// any degenerate edges in a given layer unless it has at least one
+// non-degenerate edge in that layer as well. (Note that usually there will
+// not be any degenerate edges at all, since most layer types discard them.)
+//
+// The last condition is necessary to prevent the following: suppose that one
+// layer has a chain ABC and another layer has a degenerate edge BB (with no
+// other edges incident to B). Then we can't simplify ABC to AC because there
+// would be no suitable replacement for the edge BB (since the input edge that
+// mapped to BB can't be replaced by any of the edges AA, AC, or CC without
+// moving further than snap_radius).
+class S2Builder::EdgeChainSimplifier::InteriorVertexMatcher {
+ public:
+ // Checks whether "v0" can be an interior vertex of an edge chain.
+ explicit InteriorVertexMatcher(VertexId v0)
+ : v0_(v0), v1_(-1), v2_(-1), n0_(0), n1_(0), n2_(0), excess_out_(0),
+ too_many_endpoints_(false) {
+ }
+
+ // Starts analyzing the edges of a new layer.
+ void StartLayer() {
+ excess_out_ = n0_ = n1_ = n2_ = 0;
+ }
+
+ // This method should be called for each edge incident to "v0" in a given
+ // layer. (For degenerate edges, it should be called twice.)
+ void Tally(VertexId v, bool outgoing) {
+ excess_out_ += outgoing ? 1 : -1; // outdegree - indegree
+ if (v == v0_) {
+ ++n0_; // Counts both endpoints of each degenerate edge.
+ } else {
+ // We keep track of the total number of edges (incoming or outgoing)
+ // connecting v0 to up to two adjacent vertices.
+ if (v1_ < 0) v1_ = v;
+ if (v1_ == v) {
+ ++n1_;
+ } else {
+ if (v2_ < 0) v2_ = v;
+ if (v2_ == v) {
+ ++n2_;
+ } else {
+ too_many_endpoints_ = true;
+ }
+ }
+ }
+ }
+
+ // This method should be called after processing the edges for each layer.
+ // It returns true if "v0" is an interior vertex based on the edges so far.
+ bool Matches() const {
+ // We check that there are the same number of incoming and outgoing edges
+ // in each direction by verifying that (1) indegree(v0) == outdegree(v0)
+ // and (2) the total number of edges (incoming and outgoing) to "v1" and
+ // "v2" are equal. We also check the condition on degenerate edges that
+ // is documented above.
+ return (!too_many_endpoints_ && excess_out_ == 0 && n1_ == n2_ &&
+ (n0_ == 0 || n1_ > 0));
+ }
+
+ private:
+ VertexId v0_, v1_, v2_;
+ int n0_, n1_, n2_;
+ int excess_out_; // outdegree(v0) - indegree(v0)
+ bool too_many_endpoints_; // Have we seen more than two adjacent vertices?
+};
+
+// Returns true if VertexId "v" can be an interior vertex of a simplified edge
+// chain. (See the InteriorVertexMatcher class for what this implies.)
+bool S2Builder::EdgeChainSimplifier::IsInterior(VertexId v) {
+ // Check a few simple prerequisites.
+ if (out_.degree(v) == 0) return false;
+ if (out_.degree(v) != in_.degree(v)) return false;
+ if (v < builder_.num_forced_sites_) return false; // Keep forced vertices.
+
+ // Sort the edges so that they are grouped by layer.
+ vector<EdgeId>& edges = tmp_edges_; // Avoid allocating each time.
+ edges.clear();
+ for (EdgeId e : out_.edge_ids(v)) edges.push_back(e);
+ for (EdgeId e : in_.edge_ids(v)) edges.push_back(e);
+ std::sort(edges.begin(), edges.end(), [this](EdgeId x, EdgeId y) {
+ return graph_edge_layer(x) < graph_edge_layer(y);
+ });
+ // Now feed the edges in each layer to the InteriorVertexMatcher.
+ InteriorVertexMatcher matcher(v);
+ for (auto e = edges.begin(); e != edges.end(); ) {
+ int layer = graph_edge_layer(*e);
+ matcher.StartLayer();
+ for (; e != edges.end() && graph_edge_layer(*e) == layer; ++e) {
+ Edge edge = g_.edge(*e);
+ if (edge.first == v) matcher.Tally(edge.second, true /*outgoing*/);
+ if (edge.second == v) matcher.Tally(edge.first, false /*outgoing*/);
+ }
+ if (!matcher.Matches()) return false;
+ }
+ return true;
+}
+
+// Follows the edge chain starting with (v0, v1) until either we find a
+// non-interior vertex or we return to the original vertex v0. At each vertex
+// we simplify a subchain of edges that is as long as possible.
+void S2Builder::EdgeChainSimplifier::SimplifyChain(VertexId v0, VertexId v1) {
+ // Avoid allocating "chain" each time by reusing it.
+ vector<VertexId>& chain = tmp_vertices_;
+ S2PolylineSimplifier simplifier;
+ VertexId vstart = v0;
+ bool done = false;
+ do {
+ // Simplify a subchain of edges starting (v0, v1).
+ simplifier.Init(g_.vertex(v0));
+ AvoidSites(v0, v0, v1, &simplifier);
+ chain.push_back(v0);
+ do {
+ chain.push_back(v1);
+ done = !is_interior_[v1] || v1 == vstart;
+ if (done) break;
+
+ // Attempt to extend the chain to the next vertex.
+ VertexId vprev = v0;
+ v0 = v1;
+ v1 = FollowChain(vprev, v0);
+ } while (TargetInputVertices(v0, &simplifier) &&
+ AvoidSites(chain[0], v0, v1, &simplifier) &&
+ simplifier.Extend(g_.vertex(v1)));
+
+ if (chain.size() == 2) {
+ OutputAllEdges(chain[0], chain[1]); // Could not simplify.
+ } else {
+ MergeChain(chain);
+ }
+ // Note that any degenerate edges that were not merged into a chain are
+ // output by EdgeChainSimplifier::Run().
+ chain.clear();
+ } while (!done);
+}
+
+// Given an edge (v0, v1) where v1 is an interior vertex, returns the (unique)
+// next vertex in the edge chain.
+S2Builder::Graph::VertexId S2Builder::EdgeChainSimplifier::FollowChain(
+ VertexId v0, VertexId v1) const {
+ S2_DCHECK(is_interior_[v1]);
+ for (EdgeId e : out_.edge_ids(v1)) {
+ VertexId v = g_.edge(e).second;
+ if (v != v0 && v != v1) return v;
+ }
+ S2_LOG(FATAL) << "Could not find next edge in edge chain";
+}
+
+// Copies all input edges between v0 and v1 (in both directions) to the output.
+void S2Builder::EdgeChainSimplifier::OutputAllEdges(VertexId v0, VertexId v1) {
+ for (EdgeId e : out_.edge_ids(v0, v1)) OutputEdge(e);
+ for (EdgeId e : out_.edge_ids(v1, v0)) OutputEdge(e);
+}
+
+// Ensures that the simplified edge passes within "edge_snap_radius" of all
+// the *input* vertices that snapped to the given vertex "v".
+bool S2Builder::EdgeChainSimplifier::TargetInputVertices(
+ VertexId v, S2PolylineSimplifier* simplifier) const {
+ for (InputVertexId i : site_vertices_[v]) {
+ if (!simplifier->TargetDisc(builder_.input_vertices_[i],
+ builder_.edge_snap_radius_ca_)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// Given the starting vertex v0 and last edge (v1, v2) of an edge chain,
+// restricts the allowable range of angles in order to ensure that all sites
+// near the edge (v1, v2) are avoided by at least min_edge_vertex_separation.
+bool S2Builder::EdgeChainSimplifier::AvoidSites(
+ VertexId v0, VertexId v1, VertexId v2,
+ S2PolylineSimplifier* simplifier) const {
+ const S2Point& p0 = g_.vertex(v0);
+ const S2Point& p1 = g_.vertex(v1);
+ const S2Point& p2 = g_.vertex(v2);
+ S1ChordAngle r1(p0, p1);
+ S1ChordAngle r2(p0, p2);
+
+ // The distance from the start of the edge chain must increase monotonically
+ // for each vertex, since we don't want to simplify chains that backtrack on
+ // themselves (we want a parametric approximation, not a geometric one).
+ if (r2 < r1) return false;
+
+ // We also limit the maximum edge length in order to guarantee that the
+ // simplified edge stays with max_edge_deviation() of all the input edges
+ // that snap to it.
+ if (r2 >= builder_.min_edge_length_to_split_ca_) return false;
+
+ // Otherwise it is sufficient to consider the nearby sites (edge_sites_) for
+ // a single input edge that snapped to (v1, v2) or (v2, v1). This is
+ // because each edge has a list of all sites within (max_edge_deviation +
+ // min_edge_vertex_separation), and since the output edge is within
+ // max_edge_deviation of all input edges, this list includes all sites
+ // within min_edge_vertex_separation of the output edge.
+ //
+ // Usually there is only one edge to choose from, but it's not much more
+ // effort to choose the edge with the shortest list of edge_sites_.
+ InputEdgeId best = -1;
+ const auto& edge_sites = builder_.edge_sites_;
+ for (EdgeId e : out_.edge_ids(v1, v2)) {
+ for (InputEdgeId id : g_.input_edge_ids(e)) {
+ if (best < 0 || edge_sites[id].size() < edge_sites[best].size())
+ best = id;
+ }
+ }
+ for (EdgeId e : out_.edge_ids(v2, v1)) {
+ for (InputEdgeId id : g_.input_edge_ids(e)) {
+ if (best < 0 || edge_sites[id].size() < edge_sites[best].size())
+ best = id;
+ }
+ }
+ S2_DCHECK_GE(best, 0); // Because there is at least one outgoing edge.
+
+ for (VertexId v : edge_sites[best]) {
+ // This test is optional since these sites are excluded below anyway.
+ if (v == v0 || v == v1 || v == v2) continue;
+
+ // We are only interested in sites whose distance from "p0" is in the
+ // range (r1, r2). Sites closer than "r1" have already been processed,
+ // and sites further than "r2" aren't relevant yet.
+ const S2Point& p = g_.vertex(v);
+ S1ChordAngle r(p0, p);
+ if (r <= r1 || r >= r2) continue;
+
+ // We need to figure out whether this site is to the left or right of the
+ // edge chain. For the first edge this is easy. Otherwise, since we are
+ // only considering sites in the radius range (r1, r2), we can do this by
+ // checking whether the site is to the left of the wedge (p0, p1, p2).
+ bool disc_on_left = (v1 == v0) ? (s2pred::Sign(p1, p2, p) > 0)
+ : s2pred::OrderedCCW(p0, p2, p, p1);
+ if (!simplifier->AvoidDisc(p, builder_.min_edge_site_separation_ca_,
+ disc_on_left)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// Given the vertices in a simplified edge chain, adds the corresponding
+// simplified edge(s) to the output. Note that (1) the edge chain may exist
+// in multiple layers, (2) the edge chain may exist in both directions, (3)
+// there may be more than one copy of an edge chain (in either direction)
+// within a single layer.
+void S2Builder::EdgeChainSimplifier::MergeChain(
+ const vector<VertexId>& vertices) {
+ // Suppose that all interior vertices have M outgoing edges and N incoming
+ // edges. Our goal is to group the edges into M outgoing chains and N
+ // incoming chains, and then replace each chain by a single edge.
+ vector<vector<InputEdgeId>> merged_input_ids;
+ vector<InputEdgeId> degenerate_ids;
+ int num_out; // Edge count in the outgoing direction.
+ for (int i = 1; i < vertices.size(); ++i) {
+ VertexId v0 = vertices[i-1];
+ VertexId v1 = vertices[i];
+ auto out_edges = out_.edge_ids(v0, v1);
+ auto in_edges = out_.edge_ids(v1, v0);
+ if (i == 1) {
+ // Allocate space to store the input edge ids associated with each edge.
+ num_out = out_edges.size();
+ merged_input_ids.resize(num_out + in_edges.size());
+ for (vector<InputEdgeId>& ids : merged_input_ids) {
+ ids.reserve(vertices.size() - 1);
+ }
+ } else {
+ // For each interior vertex, we build a list of input edge ids
+ // associated with degenerate edges. Each input edge ids will be
+ // assigned to one of the output edges later. (Normally there are no
+ // degenerate edges at all since most layer types don't want them.)
+ S2_DCHECK(is_interior_[v0]);
+ for (EdgeId e : out_.edge_ids(v0, v0)) {
+ for (InputEdgeId id : g_.input_edge_ids(e)) {
+ degenerate_ids.push_back(id);
+ }
+ used_[e] = true;
+ }
+ }
+ // Because the edges were created in layer order, and all sorts used are
+ // stable, the edges are still in layer order. Therefore we can simply
+ // merge together all the edges in the same relative position.
+ int j = 0;
+ for (EdgeId e : out_edges) {
+ for (InputEdgeId id : g_.input_edge_ids(e)) {
+ merged_input_ids[j].push_back(id);
+ }
+ used_[e] = true;
+ ++j;
+ }
+ for (EdgeId e : in_edges) {
+ for (InputEdgeId id : g_.input_edge_ids(e)) {
+ merged_input_ids[j].push_back(id);
+ }
+ used_[e] = true;
+ ++j;
+ }
+ S2_DCHECK_EQ(merged_input_ids.size(), j);
+ }
+ if (!degenerate_ids.empty()) {
+ std::sort(degenerate_ids.begin(), degenerate_ids.end());
+ AssignDegenerateEdges(degenerate_ids, &merged_input_ids);
+ }
+ // Output the merged edges.
+ VertexId v0 = vertices[0], v1 = vertices[1], vb = vertices.back();
+ for (EdgeId e : out_.edge_ids(v0, v1)) {
+ new_edges_.push_back(Edge(v0, vb));
+ new_edge_layers_.push_back(graph_edge_layer(e));
+ }
+ for (EdgeId e : out_.edge_ids(v1, v0)) {
+ new_edges_.push_back(Edge(vb, v0));
+ new_edge_layers_.push_back(graph_edge_layer(e));
+ }
+ for (const auto& ids : merged_input_ids) {
+ new_input_edge_ids_.push_back(input_edge_id_set_lexicon_->Add(ids));
+ }
+}
+
+// Given a list of the input edge ids associated with degenerate edges in the
+// interior of an edge chain, assigns each input edge id to one of the output
+// edges.
+void S2Builder::EdgeChainSimplifier::AssignDegenerateEdges(
+ const vector<InputEdgeId>& degenerate_ids,
+ vector<vector<InputEdgeId>>* merged_ids) const {
+ // Each degenerate edge is assigned to an output edge in the appropriate
+ // layer. If there is more than one candidate, we use heuristics so that if
+ // the input consists of a chain of edges provided in consecutive order
+ // (some of which became degenerate), then all those input edges are
+ // assigned to the same output edge. For example, suppose that one output
+ // edge is labeled with input edges 3,4,7,8, while another output edge is
+ // labeled with input edges 50,51,54,57. Then if we encounter degenerate
+ // edges labeled with input edges 5 and 6, we would prefer to assign them to
+ // the first edge (yielding the continuous range 3,4,5,6,7,8).
+ //
+ // The heuristic below is only smart enough to handle the case where the
+ // candidate output edges have non-overlapping ranges of input edges.
+ // (Otherwise there is probably not a good heuristic for assigning the
+ // degenerate edges in any case.)
+
+ // Duplicate edge ids don't affect the heuristic below, so we don't bother
+ // removing them. (They will be removed by IdSetLexicon::Add.)
+ for (auto& ids : *merged_ids) std::sort(ids.begin(), ids.end());
+
+ // Sort the output edges by their minimum input edge id. This is sufficient
+ // for the purpose of determining which layer they belong to. With
+ // EdgeType::UNDIRECTED, some edges might not have any input edge ids (i.e.,
+ // if they consist entirely of siblings of input edges). We simply remove
+ // such edges from the lists of candidates.
+ vector<unsigned> order;
+ order.reserve(merged_ids->size());
+ for (int i = 0; i < merged_ids->size(); ++i) {
+ if (!(*merged_ids)[i].empty()) order.push_back(i);
+ }
+ std::sort(order.begin(), order.end(), [&merged_ids](int i, int j) {
+ return (*merged_ids)[i][0] < (*merged_ids)[j][0];
+ });
+
+ // Now determine where each degenerate edge should be assigned.
+ for (InputEdgeId degenerate_id : degenerate_ids) {
+ int layer = input_edge_layer(degenerate_id);
+
+ // Find the first output edge whose range of input edge ids starts after
+ // "degenerate_id". If the previous edge belongs to the correct layer,
+ // then we assign the degenerate edge to it.
+ auto it = std::upper_bound(order.begin(), order.end(), degenerate_id,
+ [&merged_ids](InputEdgeId x, unsigned y) {
+ return x < (*merged_ids)[y][0];
+ });
+ if (it != order.begin()) {
+ if ((*merged_ids)[it[-1]][0] >= layer_begins_[layer]) --it;
+ }
+ S2_DCHECK_EQ(layer, input_edge_layer((*merged_ids)[it[0]][0]));
+ (*merged_ids)[it[0]].push_back(degenerate_id);
+ }
+}
--- /dev/null
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2builder_graph.h"
+
+#include <algorithm>
+#include <limits>
+#include <memory>
+#include <numeric>
+#include <vector>
+#include "s2/base/logging.h"
+#include "s2/util/gtl/btree_map.h"
+#include "s2/id_set_lexicon.h"
+#include "s2/s2builder.h"
+#include "s2/s2error.h"
+#include "s2/s2predicates.h"
+
+using std::make_pair;
+using std::max;
+using std::min;
+using std::pair;
+using std::vector;
+
+using Graph = S2Builder::Graph;
+using GraphOptions = S2Builder::GraphOptions;
+using DegenerateEdges = GraphOptions::DegenerateEdges;
+using DuplicateEdges = GraphOptions::DuplicateEdges;
+using SiblingPairs = GraphOptions::SiblingPairs;
+
+Graph::Graph(const GraphOptions& options,
+ const vector<S2Point>* vertices,
+ const vector<Edge>* edges,
+ const vector<InputEdgeIdSetId>* input_edge_id_set_ids,
+ const IdSetLexicon* input_edge_id_set_lexicon,
+ const vector<LabelSetId>* label_set_ids,
+ const IdSetLexicon* label_set_lexicon,
+ IsFullPolygonPredicate is_full_polygon_predicate)
+ : options_(options), num_vertices_(vertices->size()), vertices_(vertices),
+ edges_(edges), input_edge_id_set_ids_(input_edge_id_set_ids),
+ input_edge_id_set_lexicon_(input_edge_id_set_lexicon),
+ label_set_ids_(label_set_ids),
+ label_set_lexicon_(label_set_lexicon),
+ is_full_polygon_predicate_(std::move(is_full_polygon_predicate)) {
+ S2_DCHECK(std::is_sorted(edges->begin(), edges->end()));
+ S2_DCHECK_EQ(edges->size(), input_edge_id_set_ids->size());
+}
+
+vector<Graph::EdgeId> Graph::GetInEdgeIds() const {
+ vector<EdgeId> in_edge_ids(num_edges());
+ std::iota(in_edge_ids.begin(), in_edge_ids.end(), 0);
+ std::sort(in_edge_ids.begin(), in_edge_ids.end(),
+ [this](EdgeId ai, EdgeId bi) {
+ return StableLessThan(reverse(edge(ai)), reverse(edge(bi)), ai, bi);
+ });
+ return in_edge_ids;
+}
+
+vector<Graph::EdgeId> Graph::GetSiblingMap() const {
+ vector<EdgeId> in_edge_ids = GetInEdgeIds();
+ MakeSiblingMap(&in_edge_ids);
+ return in_edge_ids;
+}
+
+void Graph::MakeSiblingMap(vector<Graph::EdgeId>* in_edge_ids) const {
+ S2_DCHECK(options_.sibling_pairs() == SiblingPairs::REQUIRE ||
+ options_.sibling_pairs() == SiblingPairs::CREATE ||
+ options_.edge_type() == EdgeType::UNDIRECTED);
+ for (EdgeId e = 0; e < num_edges(); ++e) {
+ S2_DCHECK(edge(e) == reverse(edge((*in_edge_ids)[e])));
+ }
+ if (options_.edge_type() == EdgeType::DIRECTED) return;
+ if (options_.degenerate_edges() == DegenerateEdges::DISCARD) return;
+
+ for (EdgeId e = 0; e < num_edges(); ++e) {
+ VertexId v = edge(e).first;
+ if (edge(e).second == v) {
+ S2_DCHECK_LT(e + 1, num_edges());
+ S2_DCHECK_EQ(edge(e + 1).first, v);
+ S2_DCHECK_EQ(edge(e + 1).second, v);
+ S2_DCHECK_EQ((*in_edge_ids)[e], e);
+ S2_DCHECK_EQ((*in_edge_ids)[e + 1], e + 1);
+ (*in_edge_ids)[e] = e + 1;
+ (*in_edge_ids)[e + 1] = e;
+ ++e;
+ }
+ }
+}
+
+void Graph::VertexOutMap::Init(const Graph& g) {
+ edges_ = &g.edges();
+ edge_begins_.reserve(g.num_vertices() + 1);
+ EdgeId e = 0;
+ for (VertexId v = 0; v <= g.num_vertices(); ++v) {
+ while (e < g.num_edges() && g.edge(e).first < v) ++e;
+ edge_begins_.push_back(e);
+ }
+}
+
+void Graph::VertexInMap::Init(const Graph& g) {
+ in_edge_ids_ = g.GetInEdgeIds();
+ in_edge_begins_.reserve(g.num_vertices() + 1);
+ EdgeId e = 0;
+ for (VertexId v = 0; v <= g.num_vertices(); ++v) {
+ while (e < g.num_edges() && g.edge(in_edge_ids_[e]).second < v) ++e;
+ in_edge_begins_.push_back(e);
+ }
+}
+
+void Graph::LabelFetcher::Init(const Graph& g, S2Builder::EdgeType edge_type) {
+ g_ = &g;
+ edge_type_ = edge_type;
+ if (edge_type == EdgeType::UNDIRECTED) sibling_map_ = g.GetSiblingMap();
+}
+
+void Graph::LabelFetcher::Fetch(EdgeId e, vector<S2Builder::Label>* labels) {
+ labels->clear();
+ for (InputEdgeId input_edge_id : g_->input_edge_ids(e)) {
+ for (Label label : g_->labels(input_edge_id)) {
+ labels->push_back(label);
+ }
+ }
+ if (edge_type_ == EdgeType::UNDIRECTED) {
+ for (InputEdgeId input_edge_id : g_->input_edge_ids(sibling_map_[e])) {
+ for (Label label : g_->labels(input_edge_id)) {
+ labels->push_back(label);
+ }
+ }
+ }
+ if (labels->size() > 1) {
+ std::sort(labels->begin(), labels->end());
+ labels->erase(std::unique(labels->begin(), labels->end()), labels->end());
+ }
+}
+
+S2Builder::InputEdgeId Graph::min_input_edge_id(EdgeId e) const {
+ IdSetLexicon::IdSet id_set = input_edge_ids(e);
+ return (id_set.size() == 0) ? kNoInputEdgeId : *id_set.begin();
+}
+
+vector<S2Builder::InputEdgeId> Graph::GetMinInputEdgeIds() const {
+ vector<InputEdgeId> min_input_ids(num_edges());
+ for (EdgeId e = 0; e < num_edges(); ++e) {
+ min_input_ids[e] = min_input_edge_id(e);
+ }
+ return min_input_ids;
+}
+
+vector<Graph::EdgeId> Graph::GetInputEdgeOrder(
+ const vector<InputEdgeId>& input_ids) const {
+ vector<EdgeId> order(input_ids.size());
+ std::iota(order.begin(), order.end(), 0);
+ std::sort(order.begin(), order.end(), [&input_ids](EdgeId a, EdgeId b) {
+ // Comparison function ensures sort is stable.
+ return make_pair(input_ids[a], a) < make_pair(input_ids[b], b);
+ });
+ return order;
+}
+
+// A struct for sorting the incoming and outgoing edges around a vertex "v0".
+struct VertexEdge {
+ VertexEdge(bool _incoming, Graph::EdgeId _index,
+ Graph::VertexId _endpoint, int32 _rank)
+ : incoming(_incoming), index(_index),
+ endpoint(_endpoint), rank(_rank) {
+ }
+ bool incoming; // Is this an incoming edge to "v0"?
+ Graph::EdgeId index; // Index of this edge in "edges_" or "in_edge_ids"
+ Graph::VertexId endpoint; // The other (not "v0") endpoint of this edge
+ int32 rank; // Secondary key for edges with the same endpoint
+};
+
+// Given a set of duplicate outgoing edges (v0, v1) and a set of duplicate
+// incoming edges (v1, v0), this method assigns each edge an integer "rank" so
+// that the edges are sorted in a consistent order with respect to their
+// orderings around "v0" and "v1". Usually there is just one edge, in which
+// case this is easy. Sometimes there is one edge in each direction, in which
+// case the outgoing edge is always ordered before the incoming edge.
+//
+// In general, we allow any number of duplicate edges in each direction, in
+// which case outgoing edges are interleaved with incoming edges so as to
+// create as many degenerate (two-edge) loops as possible. In order to get a
+// consistent ordering around "v0" and "v1", we move forwards through the list
+// of outgoing edges and backwards through the list of incoming edges. If
+// there are more incoming edges, they go at the beginning of the ordering,
+// while if there are more outgoing edges then they go at the end.
+//
+// For example, suppose there are 2 edges "a,b" from "v0" to "v1", and 4 edges
+// "w,x,y,z" from "v1" to "v0". Using lower/upper case letters to represent
+// incoming/outgoing edges, the clockwise ordering around v0 would be zyAxBw,
+// and the clockwise ordering around v1 would be WbXaYZ. (Try making a
+// diagram with each edge as a separate arc.)
+static void AddVertexEdges(Graph::EdgeId out_begin, Graph::EdgeId out_end,
+ Graph::EdgeId in_begin, Graph::EdgeId in_end,
+ Graph::VertexId v1, vector<VertexEdge>* v0_edges) {
+ int rank = 0;
+ // Any extra incoming edges go at the beginning of the ordering.
+ while (in_end - in_begin > out_end - out_begin) {
+ v0_edges->push_back(VertexEdge(true, --in_end, v1, rank++));
+ }
+ // Next we interleave as many outgoing and incoming edges as possible.
+ while (in_end > in_begin) {
+ v0_edges->push_back(VertexEdge(false, out_begin++, v1, rank++));
+ v0_edges->push_back(VertexEdge(true, --in_end, v1, rank++));
+ }
+ // Any extra outgoing edges to at the end of the ordering.
+ while (out_end > out_begin) {
+ v0_edges->push_back(VertexEdge(false, out_begin++, v1, rank++));
+ }
+}
+
+bool Graph::GetLeftTurnMap(const vector<EdgeId>& in_edge_ids,
+ vector<EdgeId>* left_turn_map,
+ S2Error* error) const {
+ left_turn_map->assign(num_edges(), -1);
+ if (num_edges() == 0) return true;
+
+ // Declare vectors outside the loop to avoid reallocating them each time.
+ vector<VertexEdge> v0_edges;
+ vector<EdgeId> e_in, e_out;
+
+ // Walk through the two sorted arrays of edges (outgoing and incoming) and
+ // gather all the edges incident to each vertex. Then we sort those edges
+ // and add an entry to the left turn map from each incoming edge to the
+ // immediately following outgoing edge in clockwise order.
+ int out = 0, in = 0;
+ const Edge* out_edge = &edge(out);
+ const Edge* in_edge = &edge(in_edge_ids[in]);
+ Edge sentinel(num_vertices(), num_vertices());
+ Edge min_edge = min(*out_edge, reverse(*in_edge));
+ while (min_edge != sentinel) {
+ // Gather all incoming and outgoing edges around vertex "v0".
+ VertexId v0 = min_edge.first;
+ for (; min_edge.first == v0; min_edge = min(*out_edge, reverse(*in_edge))) {
+ VertexId v1 = min_edge.second;
+ // Count the number of copies of "min_edge" in each direction.
+ int out_begin = out, in_begin = in;
+ while (*out_edge == min_edge) {
+ out_edge = (++out == num_edges()) ? &sentinel : &edge(out);
+ }
+ while (reverse(*in_edge) == min_edge) {
+ in_edge = (++in == num_edges()) ? &sentinel : &edge(in_edge_ids[in]);
+ }
+ if (v0 != v1) {
+ AddVertexEdges(out_begin, out, in_begin, in, v1, &v0_edges);
+ } else {
+ // Each degenerate edge becomes its own loop.
+ for (; in_begin < in; ++in_begin) {
+ (*left_turn_map)[in_begin] = in_begin;
+ }
+ }
+ }
+ if (v0_edges.empty()) continue;
+
+ // Sort the edges in clockwise order around "v0".
+ VertexId min_endpoint = v0_edges.front().endpoint;
+ std::sort(v0_edges.begin() + 1, v0_edges.end(),
+ [v0, min_endpoint, this](const VertexEdge& a,
+ const VertexEdge& b) {
+ if (a.endpoint == b.endpoint) return a.rank < b.rank;
+ if (a.endpoint == min_endpoint) return true;
+ if (b.endpoint == min_endpoint) return false;
+ return !s2pred::OrderedCCW(vertex(a.endpoint), vertex(b.endpoint),
+ vertex(min_endpoint), vertex(v0));
+ });
+ // Match incoming with outgoing edges. We do this by keeping a stack of
+ // unmatched incoming edges. We also keep a stack of outgoing edges with
+ // no previous incoming edge, and match these at the end by wrapping
+ // around circularly to the start of the edge ordering.
+ for (const VertexEdge& e : v0_edges) {
+ if (e.incoming) {
+ e_in.push_back(in_edge_ids[e.index]);
+ } else if (!e_in.empty()) {
+ (*left_turn_map)[e_in.back()] = e.index;
+ e_in.pop_back();
+ } else {
+ e_out.push_back(e.index); // Matched below.
+ }
+ }
+ // Pair up additional edges using the fact that the ordering is circular.
+ std::reverse(e_out.begin(), e_out.end());
+ for (; !e_out.empty() && !e_in.empty(); e_out.pop_back(), e_in.pop_back()) {
+ (*left_turn_map)[e_in.back()] = e_out.back();
+ }
+ // We only need to process unmatched incoming edges, since we are only
+ // responsible for creating left turn map entries for those edges.
+ if (!e_in.empty() && error->ok()) {
+ error->Init(S2Error::BUILDER_EDGES_DO_NOT_FORM_LOOPS,
+ "Given edges do not form loops (indegree != outdegree)");
+ }
+ e_in.clear();
+ e_out.clear();
+ v0_edges.clear();
+ }
+ return error->ok();
+}
+
+void Graph::CanonicalizeLoopOrder(const vector<InputEdgeId>& min_input_ids,
+ vector<EdgeId>* loop) {
+ if (loop->empty()) return;
+ // Find the position of the element with the highest input edge id. If
+ // there are multiple such elements together (i.e., the edge was split
+ // into several pieces by snapping it to several vertices), then we choose
+ // the last such position in cyclic order (this attempts to preserve the
+ // original loop order even when new vertices are added). For example, if
+ // the input edge id sequence is (7, 7, 4, 5, 6, 7) then we would rotate
+ // it to obtain (4, 5, 6, 7, 7, 7).
+
+ // The reason that we put the highest-numbered edge last, rather than the
+ // lowest-numbered edge first, is that S2Loop::Invert() reverses the loop
+ // edge order *except* for the last edge. For example, the loop ABCD (with
+ // edges AB, BC, CD, DA) becomes DCBA (with edges DC, CB, BA, AD). Note
+ // that the last edge is the same except for its direction (DA vs. AD).
+ // This has the advantage that if an undirected loop is assembled with the
+ // wrong orientation and later inverted (e.g. by S2Polygon::InitOriented),
+ // we still end up preserving the original cyclic vertex order.
+ int pos = 0;
+ bool saw_gap = false;
+ for (int i = 1; i < loop->size(); ++i) {
+ int cmp = min_input_ids[(*loop)[i]] - min_input_ids[(*loop)[pos]];
+ if (cmp < 0) {
+ saw_gap = true;
+ } else if (cmp > 0 || !saw_gap) {
+ pos = i;
+ saw_gap = false;
+ }
+ }
+ if (++pos == loop->size()) pos = 0; // Convert loop end to loop start.
+ std::rotate(loop->begin(), loop->begin() + pos, loop->end());
+}
+
+void Graph::CanonicalizeVectorOrder(const vector<InputEdgeId>& min_input_ids,
+ vector<vector<EdgeId>>* chains) {
+ std::sort(chains->begin(), chains->end(),
+ [&min_input_ids](const vector<EdgeId>& a, const vector<EdgeId>& b) {
+ return min_input_ids[a[0]] < min_input_ids[b[0]];
+ });
+}
+
+bool Graph::GetDirectedLoops(LoopType loop_type, vector<EdgeLoop>* loops,
+ S2Error* error) const {
+ S2_DCHECK(options_.degenerate_edges() == DegenerateEdges::DISCARD ||
+ options_.degenerate_edges() == DegenerateEdges::DISCARD_EXCESS);
+ S2_DCHECK(options_.edge_type() == EdgeType::DIRECTED);
+
+ vector<EdgeId> left_turn_map;
+ if (!GetLeftTurnMap(GetInEdgeIds(), &left_turn_map, error)) return false;
+ vector<InputEdgeId> min_input_ids = GetMinInputEdgeIds();
+
+ // If we are breaking loops at repeated vertices, we maintain a map from
+ // VertexId to its position in "path".
+ vector<int> path_index;
+ if (loop_type == LoopType::SIMPLE) path_index.assign(num_vertices(), -1);
+
+ // Visit edges in arbitrary order, and try to build a loop from each edge.
+ vector<EdgeId> path;
+ for (EdgeId start = 0; start < num_edges(); ++start) {
+ if (left_turn_map[start] < 0) continue;
+
+ // Build a loop by making left turns at each vertex until we return to
+ // "start". We use "left_turn_map" to keep track of which edges have
+ // already been visited by setting its entries to -1 as we go along. If
+ // we are building vertex cycles, then whenever we encounter a vertex that
+ // is already part of the path, we "peel off" a loop by removing those
+ // edges from the path so far.
+ for (EdgeId e = start, next; left_turn_map[e] >= 0; e = next) {
+ path.push_back(e);
+ next = left_turn_map[e];
+ left_turn_map[e] = -1;
+ if (loop_type == LoopType::SIMPLE) {
+ path_index[edge(e).first] = path.size() - 1;
+ int loop_start = path_index[edge(e).second];
+ if (loop_start < 0) continue;
+ // Peel off a loop from the path.
+ vector<EdgeId> loop(path.begin() + loop_start, path.end());
+ path.erase(path.begin() + loop_start, path.end());
+ for (EdgeId e2 : loop) path_index[edge(e2).first] = -1;
+ CanonicalizeLoopOrder(min_input_ids, &loop);
+ loops->push_back(std::move(loop));
+ }
+ }
+ if (loop_type == LoopType::SIMPLE) {
+ S2_DCHECK(path.empty()); // Invariant.
+ } else {
+ CanonicalizeLoopOrder(min_input_ids, &path);
+ loops->push_back(std::move(path));
+ path.clear();
+ }
+ }
+ CanonicalizeVectorOrder(min_input_ids, loops);
+ return true;
+}
+
+bool Graph::GetDirectedComponents(
+ DegenerateBoundaries degenerate_boundaries,
+ vector<DirectedComponent>* components, S2Error* error) const {
+ S2_DCHECK(options_.degenerate_edges() == DegenerateEdges::DISCARD ||
+ (options_.degenerate_edges() == DegenerateEdges::DISCARD_EXCESS &&
+ degenerate_boundaries == DegenerateBoundaries::KEEP));
+ S2_DCHECK(options_.sibling_pairs() == SiblingPairs::REQUIRE ||
+ options_.sibling_pairs() == SiblingPairs::CREATE);
+ S2_DCHECK(options_.edge_type() == EdgeType::DIRECTED); // Implied by above.
+
+ vector<EdgeId> sibling_map = GetInEdgeIds();
+ vector<EdgeId> left_turn_map;
+ if (!GetLeftTurnMap(sibling_map, &left_turn_map, error)) return false;
+ MakeSiblingMap(&sibling_map);
+ vector<InputEdgeId> min_input_ids = GetMinInputEdgeIds();
+ vector<EdgeId> frontier; // Unexplored sibling edges.
+
+ // A map from EdgeId to the position of that edge in "path". Only needed if
+ // degenerate boundaries are being discarded.
+ vector<int> path_index;
+ if (degenerate_boundaries == DegenerateBoundaries::DISCARD) {
+ path_index.assign(num_edges(), -1);
+ }
+ for (EdgeId min_start = 0; min_start < num_edges(); ++min_start) {
+ if (left_turn_map[min_start] < 0) continue; // Already used.
+
+ // Build a connected component by keeping a stack of unexplored siblings
+ // of the edges used so far.
+ DirectedComponent component;
+ frontier.push_back(min_start);
+ while (!frontier.empty()) {
+ EdgeId start = frontier.back();
+ frontier.pop_back();
+ if (left_turn_map[start] < 0) continue; // Already used.
+
+ // Build a path by making left turns at each vertex until we return to
+ // "start". Whenever we encounter an edge that is a sibling of an edge
+ // that is already on the path, we "peel off" a loop consisting of any
+ // edges that were between these two edges.
+ vector<EdgeId> path;
+ for (EdgeId e = start, next; left_turn_map[e] >= 0; e = next) {
+ path.push_back(e);
+ next = left_turn_map[e];
+ left_turn_map[e] = -1;
+ // If the sibling hasn't been visited yet, add it to the frontier.
+ EdgeId sibling = sibling_map[e];
+ if (left_turn_map[sibling] >= 0) {
+ frontier.push_back(sibling);
+ }
+ if (degenerate_boundaries == DegenerateBoundaries::DISCARD) {
+ path_index[e] = path.size() - 1;
+ int sibling_index = path_index[sibling];
+ if (sibling_index < 0) continue;
+
+ // Common special case: the edge and its sibling are adjacent, in
+ // which case we can simply remove them from the path and continue.
+ if (sibling_index == path.size() - 2) {
+ path.resize(sibling_index);
+ // We don't need to update "path_index" for these two edges
+ // because both edges of the sibling pair have now been used.
+ continue;
+ }
+ // Peel off a loop from the path.
+ vector<EdgeId> loop(path.begin() + sibling_index + 1, path.end() - 1);
+ path.erase(path.begin() + sibling_index, path.end());
+ // Mark the edges that are no longer part of the path.
+ for (EdgeId e2 : loop) path_index[e2] = -1;
+ CanonicalizeLoopOrder(min_input_ids, &loop);
+ component.push_back(std::move(loop));
+ }
+ }
+ // Mark the edges that are no longer part of the path.
+ if (degenerate_boundaries == DegenerateBoundaries::DISCARD) {
+ for (EdgeId e2 : path) path_index[e2] = -1;
+ }
+ CanonicalizeLoopOrder(min_input_ids, &path);
+ component.push_back(std::move(path));
+ }
+ CanonicalizeVectorOrder(min_input_ids, &component);
+ components->push_back(std::move(component));
+ }
+ // Sort the components to correspond to the input edge ordering.
+ std::sort(components->begin(), components->end(),
+ [&min_input_ids](const DirectedComponent& a,
+ const DirectedComponent& b) {
+ return min_input_ids[a[0][0]] < min_input_ids[b[0][0]];
+ });
+ return true;
+}
+
+// Encodes the index of one of the two complements of each component
+// (a.k.a. the "slot", either 0 or 1) as a negative EdgeId.
+inline static Graph::EdgeId MarkEdgeUsed(int slot) { return -1 - slot; }
+
+bool Graph::GetUndirectedComponents(LoopType loop_type,
+ vector<UndirectedComponent>* components,
+ S2Error* error) const {
+ S2_DCHECK(options_.degenerate_edges() == DegenerateEdges::DISCARD ||
+ options_.degenerate_edges() == DegenerateEdges::DISCARD_EXCESS);
+ S2_DCHECK(options_.edge_type() == EdgeType::UNDIRECTED);
+
+ vector<EdgeId> sibling_map = GetInEdgeIds();
+ vector<EdgeId> left_turn_map;
+ if (!GetLeftTurnMap(sibling_map, &left_turn_map, error)) return false;
+ MakeSiblingMap(&sibling_map);
+ vector<InputEdgeId> min_input_ids = GetMinInputEdgeIds();
+
+ // A stack of unexplored sibling edges. Each sibling edge has a "slot"
+ // (0 or 1) that indicates which of the two complements it belongs to.
+ vector<pair<EdgeId, int>> frontier;
+
+ // If we are breaking loops at repeated vertices, we maintain a map from
+ // VertexId to its position in "path".
+ vector<int> path_index;
+ if (loop_type == LoopType::SIMPLE) path_index.assign(num_vertices(), -1);
+
+ for (EdgeId min_start = 0; min_start < num_edges(); ++min_start) {
+ if (left_turn_map[min_start] < 0) continue; // Already used.
+
+ // Build a connected component by keeping a stack of unexplored siblings
+ // of the edges used so far.
+ UndirectedComponent component;
+ frontier.push_back(make_pair(min_start, 0));
+ while (!frontier.empty()) {
+ EdgeId start = frontier.back().first;
+ int slot = frontier.back().second;
+ frontier.pop_back();
+ if (left_turn_map[start] < 0) continue; // Already used.
+
+ // Build a path by making left turns at each vertex until we return to
+ // "start". We use "left_turn_map" to keep track of which edges have
+ // already been visited, and which complement they were assigned to, by
+ // setting its entries to negative values as we go along.
+ vector<EdgeId> path;
+ for (EdgeId e = start, next; left_turn_map[e] >= 0; e = next) {
+ path.push_back(e);
+ next = left_turn_map[e];
+ left_turn_map[e] = MarkEdgeUsed(slot);
+ // If the sibling hasn't been visited yet, add it to the frontier.
+ EdgeId sibling = sibling_map[e];
+ if (left_turn_map[sibling] >= 0) {
+ frontier.push_back(make_pair(sibling, 1 - slot));
+ } else if (left_turn_map[sibling] != MarkEdgeUsed(1 - slot)) {
+ // Two siblings edges can only belong the same complement if the
+ // given undirected edges do not form loops.
+ error->Init(S2Error::BUILDER_EDGES_DO_NOT_FORM_LOOPS,
+ "Given undirected edges do not form loops");
+ return false;
+ }
+ if (loop_type == LoopType::SIMPLE) {
+ // Whenever we encounter a vertex that is already part of the path,
+ // we "peel off" a loop by removing those edges from the path.
+ path_index[edge(e).first] = path.size() - 1;
+ int loop_start = path_index[edge(e).second];
+ if (loop_start < 0) continue;
+ vector<EdgeId> loop(path.begin() + loop_start, path.end());
+ path.erase(path.begin() + loop_start, path.end());
+ // Mark the vertices that are no longer part of the path.
+ for (EdgeId e2 : loop) path_index[edge(e2).first] = -1;
+ CanonicalizeLoopOrder(min_input_ids, &loop);
+ component[slot].push_back(std::move(loop));
+ }
+ }
+ if (loop_type == LoopType::SIMPLE) {
+ S2_DCHECK(path.empty()); // Invariant.
+ } else {
+ CanonicalizeLoopOrder(min_input_ids, &path);
+ component[slot].push_back(std::move(path));
+ }
+ }
+ CanonicalizeVectorOrder(min_input_ids, &component[0]);
+ CanonicalizeVectorOrder(min_input_ids, &component[1]);
+ // To save some work in S2PolygonLayer, we swap the two loop sets of the
+ // component so that the loop set whose first loop most closely follows
+ // the input edge ordering is first. (If the input was a valid S2Polygon,
+ // then this component will contain normalized loops.)
+ if (min_input_ids[component[0][0][0]] > min_input_ids[component[1][0][0]]) {
+ component[0].swap(component[1]);
+ }
+ components->push_back(std::move(component));
+ }
+ // Sort the components to correspond to the input edge ordering.
+ std::sort(components->begin(), components->end(),
+ [&min_input_ids](const UndirectedComponent& a,
+ const UndirectedComponent& b) {
+ return min_input_ids[a[0][0][0]] < min_input_ids[b[0][0][0]];
+ });
+ return true;
+}
+
+class Graph::PolylineBuilder {
+ public:
+ explicit PolylineBuilder(const Graph& g);
+ vector<EdgePolyline> BuildPaths();
+ vector<EdgePolyline> BuildWalks();
+
+ private:
+ bool is_interior(VertexId v);
+ int excess_degree(VertexId v);
+ EdgePolyline BuildPath(EdgeId e);
+ EdgePolyline BuildWalk(VertexId v);
+ void MaximizeWalk(EdgePolyline* polyline);
+
+ const Graph& g_;
+ Graph::VertexInMap in_;
+ Graph::VertexOutMap out_;
+ vector<EdgeId> sibling_map_;
+ vector<InputEdgeId> min_input_ids_;
+ bool directed_;
+ int edges_left_;
+ vector<bool> used_;
+ // A map of (outdegree(v) - indegree(v)) considering used edges only.
+ gtl::btree_map<VertexId, int> excess_used_;
+};
+
+vector<Graph::EdgePolyline> Graph::GetPolylines(
+ PolylineType polyline_type) const {
+ S2_DCHECK(options_.sibling_pairs() == SiblingPairs::DISCARD ||
+ options_.sibling_pairs() == SiblingPairs::DISCARD_EXCESS ||
+ options_.sibling_pairs() == SiblingPairs::KEEP);
+ PolylineBuilder builder(*this);
+ if (polyline_type == PolylineType::PATH) {
+ return builder.BuildPaths();
+ } else {
+ return builder.BuildWalks();
+ }
+}
+
+Graph::PolylineBuilder::PolylineBuilder(const Graph& g)
+ : g_(g), in_(g), out_(g),
+ min_input_ids_(g.GetMinInputEdgeIds()),
+ directed_(g_.options().edge_type() == EdgeType::DIRECTED),
+ edges_left_(g.num_edges() / (directed_ ? 1 : 2)),
+ used_(g.num_edges(), false) {
+ if (!directed_) {
+ sibling_map_ = in_.in_edge_ids();
+ g.MakeSiblingMap(&sibling_map_);
+ }
+}
+
+inline bool Graph::PolylineBuilder::is_interior(VertexId v) {
+ if (directed_) {
+ return in_.degree(v) == 1 && out_.degree(v) == 1;
+ } else {
+ return out_.degree(v) == 2;
+ }
+}
+
+inline int Graph::PolylineBuilder::excess_degree(VertexId v) {
+ return directed_ ? out_.degree(v) - in_.degree(v) : out_.degree(v) % 2;
+}
+
+vector<Graph::EdgePolyline> Graph::PolylineBuilder::BuildPaths() {
+ // First build polylines starting at all the vertices that cannot be in the
+ // polyline interior (i.e., indegree != 1 or outdegree != 1 for directed
+ // edges, or degree != 2 for undirected edges). We consider the possible
+ // starting edges in input edge id order so that we preserve the input path
+ // direction even when undirected edges are used. (Undirected edges are
+ // represented by sibling pairs where only the edge in the input direction
+ // is labeled with an input edge id.)
+ vector<EdgePolyline> polylines;
+ vector<EdgeId> edges = g_.GetInputEdgeOrder(min_input_ids_);
+ for (EdgeId e : edges) {
+ if (!used_[e] && !is_interior(g_.edge(e).first)) {
+ polylines.push_back(BuildPath(e));
+ }
+ }
+ // If there are any edges left, they form non-intersecting loops. We build
+ // each loop and then canonicalize its edge order. We consider candidate
+ // starting edges in input edge id order in order to preserve the input
+ // direction of undirected loops. Even so, we still need to canonicalize
+ // the edge order to ensure that when an input edge is split into an edge
+ // chain, the loop does not start in the middle of such a chain.
+ for (EdgeId e : edges) {
+ if (edges_left_ == 0) break;
+ if (used_[e]) continue;
+ EdgePolyline polyline = BuildPath(e);
+ CanonicalizeLoopOrder(min_input_ids_, &polyline);
+ polylines.push_back(std::move(polyline));
+ }
+ S2_DCHECK_EQ(0, edges_left_);
+
+ // Sort the polylines to correspond to the input order (if possible).
+ CanonicalizeVectorOrder(min_input_ids_, &polylines);
+ return polylines;
+}
+
+Graph::EdgePolyline Graph::PolylineBuilder::BuildPath(EdgeId e) {
+ // We simply follow edges until either we reach a vertex where there is a
+ // choice about which way to go (where is_interior(v) is false), or we
+ // return to the starting vertex (if the polyline is actually a loop).
+ EdgePolyline polyline;
+ VertexId start = g_.edge(e).first;
+ for (;;) {
+ polyline.push_back(e);
+ S2_DCHECK(!used_[e]);
+ used_[e] = true;
+ if (!directed_) used_[sibling_map_[e]] = true;
+ --edges_left_;
+ VertexId v = g_.edge(e).second;
+ if (!is_interior(v) || v == start) break;
+ if (directed_) {
+ S2_DCHECK_EQ(1, out_.degree(v));
+ e = *out_.edge_ids(v).begin();
+ } else {
+ S2_DCHECK_EQ(2, out_.degree(v));
+ for (EdgeId e2 : out_.edge_ids(v)) if (!used_[e2]) e = e2;
+ }
+ }
+ return polyline;
+}
+
+vector<Graph::EdgePolyline> Graph::PolylineBuilder::BuildWalks() {
+ // Note that some of this code is worst-case quadratic in the maximum vertex
+ // degree. This could be fixed with a few extra arrays, but it should not
+ // be a problem in practice.
+
+ // First, build polylines from all vertices where outdegree > indegree (or
+ // for undirected edges, vertices whose degree is odd). We consider the
+ // possible starting edges in input edge id order, for idempotency in the
+ // case where multiple input polylines share vertices or edges.
+ vector<EdgePolyline> polylines;
+ vector<EdgeId> edges = g_.GetInputEdgeOrder(min_input_ids_);
+ for (EdgeId e : edges) {
+ if (used_[e]) continue;
+ VertexId v = g_.edge(e).first;
+ int excess = excess_degree(v);
+ if (excess <= 0) continue;
+ excess -= excess_used_[v];
+ if (directed_ ? (excess <= 0) : (excess % 2 == 0)) continue;
+ ++excess_used_[v];
+ polylines.push_back(BuildWalk(v));
+ --excess_used_[g_.edge(polylines.back().back()).second];
+ }
+ // Now all vertices have outdegree == indegree (or even degree if undirected
+ // edges are being used). Therefore all remaining edges can be assembled
+ // into loops. We first try to expand the existing polylines if possible by
+ // adding loops to them.
+ if (edges_left_ > 0) {
+ for (EdgePolyline& polyline : polylines) {
+ MaximizeWalk(&polyline);
+ }
+ }
+ // Finally, if there are still unused edges then we build loops. If the
+ // input is a polyline that forms a loop, then for idempotency we need to
+ // start from the edge with minimum input edge id. If the minimal input
+ // edge was split into several edges, then we start from the first edge of
+ // the chain.
+ for (int i = 0; i < edges.size() && edges_left_ > 0; ++i) {
+ EdgeId e = edges[i];
+ if (used_[e]) continue;
+
+ // Determine whether the origin of this edge is the start of an edge
+ // chain. To do this, we test whether (outdegree - indegree == 1) for the
+ // origin, considering only unused edges with the same minimum input edge
+ // id. (Undirected edges have input edge ids in one direction only.)
+ VertexId v = g_.edge(e).first;
+ InputEdgeId id = min_input_ids_[e];
+ int excess = 0;
+ for (int j = i; j < edges.size() && min_input_ids_[edges[j]] == id; ++j) {
+ EdgeId e2 = edges[j];
+ if (used_[e2]) continue;
+ if (g_.edge(e2).first == v) ++excess;
+ if (g_.edge(e2).second == v) --excess;
+ }
+ // It is also acceptable to start a polyline from any degenerate edge.
+ if (excess == 1 || g_.edge(e).second == v) {
+ EdgePolyline polyline = BuildWalk(v);
+ MaximizeWalk(&polyline);
+ polylines.push_back(std::move(polyline));
+ }
+ }
+ S2_DCHECK_EQ(0, edges_left_);
+
+ // Sort the polylines to correspond to the input order (if possible).
+ CanonicalizeVectorOrder(min_input_ids_, &polylines);
+ return polylines;
+}
+
+Graph::EdgePolyline Graph::PolylineBuilder::BuildWalk(VertexId v) {
+ EdgePolyline polyline;
+ for (;;) {
+ // Follow the edge with the smallest input edge id.
+ EdgeId best_edge = -1;
+ InputEdgeId best_out_id = std::numeric_limits<InputEdgeId>::max();
+ for (EdgeId e : out_.edge_ids(v)) {
+ if (used_[e] || min_input_ids_[e] >= best_out_id) continue;
+ best_out_id = min_input_ids_[e];
+ best_edge = e;
+ }
+ if (best_edge < 0) return polyline;
+ // For idempotency when there are multiple input polylines, we stop the
+ // walk early if "best_edge" might be a continuation of a different
+ // incoming edge.
+ int excess = excess_degree(v) - excess_used_[v];
+ if (directed_ ? (excess < 0) : (excess % 2) == 1) {
+ for (EdgeId e : in_.edge_ids(v)) {
+ if (!used_[e] && min_input_ids_[e] <= best_out_id) {
+ return polyline;
+ }
+ }
+ }
+ polyline.push_back(best_edge);
+ used_[best_edge] = true;
+ if (!directed_) used_[sibling_map_[best_edge]] = true;
+ --edges_left_;
+ v = g_.edge(best_edge).second;
+ }
+}
+
+void Graph::PolylineBuilder::MaximizeWalk(EdgePolyline* polyline) {
+ // Examine all vertices of the polyline and check whether there are any
+ // unused outgoing edges. If so, then build a loop starting at that vertex
+ // and insert it into the polyline. (The walk is guaranteed to be a loop
+ // because this method is only called when all vertices have equal numbers
+ // of unused incoming and outgoing edges.)
+ for (int i = 0; i <= polyline->size(); ++i) {
+ VertexId v = (i == 0 ? g_.edge((*polyline)[i]).first
+ : g_.edge((*polyline)[i - 1]).second);
+ for (EdgeId e : out_.edge_ids(v)) {
+ if (!used_[e]) {
+ EdgePolyline loop = BuildWalk(v);
+ S2_DCHECK_EQ(v, g_.edge(loop.back()).second);
+ polyline->insert(polyline->begin() + i, loop.begin(), loop.end());
+ S2_DCHECK(used_[e]); // All outgoing edges from "v" are now used.
+ break;
+ }
+ }
+ }
+}
+
+class Graph::EdgeProcessor {
+ public:
+ EdgeProcessor(const GraphOptions& options,
+ vector<Edge>* edges,
+ vector<InputEdgeIdSetId>* input_ids,
+ IdSetLexicon* id_set_lexicon);
+ void Run(S2Error* error);
+
+ private:
+ void AddEdge(const Edge& edge, InputEdgeIdSetId input_edge_id_set_id);
+ void AddEdges(int num_edges, const Edge& edge,
+ InputEdgeIdSetId input_edge_id_set_id);
+ void CopyEdges(int out_begin, int out_end);
+ InputEdgeIdSetId MergeInputIds(int out_begin, int out_end);
+
+ GraphOptions options_;
+ vector<Edge>& edges_;
+ vector<InputEdgeIdSetId>& input_ids_;
+ IdSetLexicon* id_set_lexicon_;
+ vector<EdgeId> out_edges_;
+ vector<EdgeId> in_edges_;
+
+ vector<Edge> new_edges_;
+ vector<InputEdgeIdSetId> new_input_ids_;
+
+ vector<InputEdgeId> tmp_ids_;
+};
+
+void Graph::ProcessEdges(
+ GraphOptions* options, std::vector<Edge>* edges,
+ std::vector<InputEdgeIdSetId>* input_ids, IdSetLexicon* id_set_lexicon,
+ S2Error* error) {
+ EdgeProcessor processor(*options, edges, input_ids, id_set_lexicon);
+ processor.Run(error);
+ // Certain values of sibling_pairs() discard half of the edges and change
+ // the edge_type() to DIRECTED (see the description of GraphOptions).
+ if (options->sibling_pairs() == SiblingPairs::REQUIRE ||
+ options->sibling_pairs() == SiblingPairs::CREATE) {
+ options->set_edge_type(EdgeType::DIRECTED);
+ }
+}
+
+Graph::EdgeProcessor::EdgeProcessor(const GraphOptions& options,
+ vector<Edge>* edges,
+ vector<InputEdgeIdSetId>* input_ids,
+ IdSetLexicon* id_set_lexicon)
+ : options_(options), edges_(*edges),
+ input_ids_(*input_ids), id_set_lexicon_(id_set_lexicon),
+ out_edges_(edges_.size()), in_edges_(edges_.size()) {
+ // Sort the outgoing and incoming edges in lexigraphic order. We use a
+ // stable sort to ensure that each undirected edge becomes a sibling pair,
+ // even if there are multiple identical input edges.
+ std::iota(out_edges_.begin(), out_edges_.end(), 0);
+ std::sort(out_edges_.begin(), out_edges_.end(), [this](EdgeId a, EdgeId b) {
+ return StableLessThan(edges_[a], edges_[b], a, b);
+ });
+ std::iota(in_edges_.begin(), in_edges_.end(), 0);
+ std::sort(in_edges_.begin(), in_edges_.end(), [this](EdgeId a, EdgeId b) {
+ return StableLessThan(reverse(edges_[a]), reverse(edges_[b]), a, b);
+ });
+ new_edges_.reserve(edges_.size());
+ new_input_ids_.reserve(edges_.size());
+}
+
+inline void Graph::EdgeProcessor::AddEdge(
+ const Edge& edge, InputEdgeIdSetId input_edge_id_set_id) {
+ new_edges_.push_back(edge);
+ new_input_ids_.push_back(input_edge_id_set_id);
+}
+
+void Graph::EdgeProcessor::AddEdges(int num_edges, const Edge& edge,
+ InputEdgeIdSetId input_edge_id_set_id) {
+ for (int i = 0; i < num_edges; ++i) {
+ AddEdge(edge, input_edge_id_set_id);
+ }
+}
+
+void Graph::EdgeProcessor::CopyEdges(int out_begin, int out_end) {
+ for (int i = out_begin; i < out_end; ++i) {
+ AddEdge(edges_[out_edges_[i]], input_ids_[out_edges_[i]]);
+ }
+}
+
+S2Builder::InputEdgeIdSetId Graph::EdgeProcessor::MergeInputIds(
+ int out_begin, int out_end) {
+ if (out_end - out_begin == 1) {
+ return input_ids_[out_edges_[out_begin]];
+ }
+ tmp_ids_.clear();
+ for (int i = out_begin; i < out_end; ++i) {
+ for (auto id : id_set_lexicon_->id_set(input_ids_[out_edges_[i]])) {
+ tmp_ids_.push_back(id);
+ }
+ }
+ return id_set_lexicon_->Add(tmp_ids_);
+}
+
+void Graph::EdgeProcessor::Run(S2Error* error) {
+ int num_edges = edges_.size();
+ if (num_edges == 0) return;
+
+ // Walk through the two sorted arrays performing a merge join. For each
+ // edge, gather all the duplicate copies of the edge in both directions
+ // (outgoing and incoming). Then decide what to do based on "options_" and
+ // how many copies of the edge there are in each direction.
+ int out = 0, in = 0;
+ const Edge* out_edge = &edges_[out_edges_[out]];
+ const Edge* in_edge = &edges_[in_edges_[in]];
+ Edge sentinel(std::numeric_limits<VertexId>::max(),
+ std::numeric_limits<VertexId>::max());
+ for (;;) {
+ Edge edge = min(*out_edge, reverse(*in_edge));
+ if (edge == sentinel) break;
+
+ int out_begin = out, in_begin = in;
+ while (*out_edge == edge) {
+ out_edge = (++out == num_edges) ? &sentinel : &edges_[out_edges_[out]];
+ }
+ while (reverse(*in_edge) == edge) {
+ in_edge = (++in == num_edges) ? &sentinel : &edges_[in_edges_[in]];
+ }
+ int n_out = out - out_begin;
+ int n_in = in - in_begin;
+ if (edge.first == edge.second) {
+ S2_DCHECK_EQ(n_out, n_in);
+ if (options_.degenerate_edges() == DegenerateEdges::DISCARD) {
+ continue;
+ }
+ if (options_.degenerate_edges() == DegenerateEdges::DISCARD_EXCESS &&
+ ((out_begin > 0 &&
+ edges_[out_edges_[out_begin - 1]].first == edge.first) ||
+ (out < num_edges && edges_[out_edges_[out]].first == edge.first) ||
+ (in_begin > 0 &&
+ edges_[in_edges_[in_begin - 1]].second == edge.first) ||
+ (in < num_edges && edges_[in_edges_[in]].second == edge.first))) {
+ continue; // There were non-degenerate incident edges, so discard.
+ }
+ if (options_.edge_type() == EdgeType::UNDIRECTED &&
+ (options_.sibling_pairs() == SiblingPairs::REQUIRE ||
+ options_.sibling_pairs() == SiblingPairs::CREATE)) {
+ // When we have undirected edges and are guaranteed to have siblings,
+ // we cut the number of edges in half (see s2builder.h).
+ S2_DCHECK_EQ(0, n_out & 1); // Number of edges is always even.
+ AddEdges(options_.duplicate_edges() == DuplicateEdges::MERGE ?
+ 1 : (n_out / 2), edge, MergeInputIds(out_begin, out));
+ } else if (options_.duplicate_edges() == DuplicateEdges::MERGE) {
+ AddEdges(options_.edge_type() == EdgeType::UNDIRECTED ? 2 : 1,
+ edge, MergeInputIds(out_begin, out));
+ } else if (options_.sibling_pairs() == SiblingPairs::DISCARD ||
+ options_.sibling_pairs() == SiblingPairs::DISCARD_EXCESS) {
+ // Any SiblingPair option that discards edges causes the labels of all
+ // duplicate edges to be merged together (see s2builder.h).
+ AddEdges(n_out, edge, MergeInputIds(out_begin, out));
+ } else {
+ CopyEdges(out_begin, out);
+ }
+ } else if (options_.sibling_pairs() == SiblingPairs::KEEP) {
+ if (n_out > 1 && options_.duplicate_edges() == DuplicateEdges::MERGE) {
+ AddEdge(edge, MergeInputIds(out_begin, out));
+ } else {
+ CopyEdges(out_begin, out);
+ }
+ } else if (options_.sibling_pairs() == SiblingPairs::DISCARD) {
+ if (options_.edge_type() == EdgeType::DIRECTED) {
+ // If n_out == n_in: balanced sibling pairs
+ // If n_out < n_in: unbalanced siblings, in the form AB, BA, BA
+ // If n_out > n_in: unbalanced siblings, in the form AB, AB, BA
+ if (n_out <= n_in) continue;
+ // Any option that discards edges causes the labels of all duplicate
+ // edges to be merged together (see s2builder.h).
+ AddEdges(options_.duplicate_edges() == DuplicateEdges::MERGE ?
+ 1 : (n_out - n_in), edge, MergeInputIds(out_begin, out));
+ } else {
+ if ((n_out & 1) == 0) continue;
+ AddEdge(edge, MergeInputIds(out_begin, out));
+ }
+ } else if (options_.sibling_pairs() == SiblingPairs::DISCARD_EXCESS) {
+ if (options_.edge_type() == EdgeType::DIRECTED) {
+ // See comments above. The only difference is that if there are
+ // balanced sibling pairs, we want to keep one such pair.
+ if (n_out < n_in) continue;
+ AddEdges(options_.duplicate_edges() == DuplicateEdges::MERGE ?
+ 1 : max(1, n_out - n_in), edge, MergeInputIds(out_begin, out));
+ } else {
+ AddEdges((n_out & 1) ? 1 : 2, edge, MergeInputIds(out_begin, out));
+ }
+ } else {
+ S2_DCHECK(options_.sibling_pairs() == SiblingPairs::REQUIRE ||
+ options_.sibling_pairs() == SiblingPairs::CREATE);
+ if (error->ok() && options_.sibling_pairs() == SiblingPairs::REQUIRE &&
+ (options_.edge_type() == EdgeType::DIRECTED ? (n_out != n_in)
+ : ((n_out & 1) != 0))) {
+ error->Init(S2Error::BUILDER_MISSING_EXPECTED_SIBLING_EDGES,
+ "Expected all input edges to have siblings, "
+ "but some were missing");
+ }
+ if (options_.duplicate_edges() == DuplicateEdges::MERGE) {
+ AddEdge(edge, MergeInputIds(out_begin, out));
+ } else if (options_.edge_type() == EdgeType::UNDIRECTED) {
+ // Convert graph to use directed edges instead (see documentation of
+ // REQUIRE/CREATE for undirected edges).
+ AddEdges((n_out + 1) / 2, edge, MergeInputIds(out_begin, out));
+ } else {
+ CopyEdges(out_begin, out);
+ if (n_in > n_out) {
+ // Automatically created edges have no input edge ids or labels.
+ AddEdges(n_in - n_out, edge, IdSetLexicon::EmptySetId());
+ }
+ }
+ }
+ }
+ edges_.swap(new_edges_);
+ edges_.shrink_to_fit();
+ input_ids_.swap(new_input_ids_);
+ input_ids_.shrink_to_fit();
+}
+
+vector<S2Point> Graph::FilterVertices(const vector<S2Point>& vertices,
+ std::vector<Edge>* edges,
+ vector<VertexId>* tmp) {
+ // Gather the vertices that are actually used.
+ vector<VertexId> used;
+ used.reserve(2 * edges->size());
+ for (const Edge& e : *edges) {
+ used.push_back(e.first);
+ used.push_back(e.second);
+ }
+ // Sort the vertices and find the distinct ones.
+ std::sort(used.begin(), used.end());
+ used.erase(std::unique(used.begin(), used.end()), used.end());
+
+ // Build the list of new vertices, and generate a map from old vertex id to
+ // new vertex id.
+ vector<VertexId>& vmap = *tmp;
+ vmap.resize(vertices.size());
+ vector<S2Point> new_vertices(used.size());
+ for (int i = 0; i < used.size(); ++i) {
+ new_vertices[i] = vertices[used[i]];
+ vmap[used[i]] = i;
+ }
+ // Update the edges.
+ for (Edge& e : *edges) {
+ e.first = vmap[e.first];
+ e.second = vmap[e.second];
+ }
+ return new_vertices;
+}
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2builderutil_closed_set_normalizer.h"
+
+#include <memory>
+
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/s2builder_layer.h"
+
+using absl::make_unique;
+using std::shared_ptr;
+using std::unique_ptr;
+using std::vector;
+
+using EdgeType = S2Builder::EdgeType;
+using Graph = S2Builder::Graph;
+using GraphOptions = S2Builder::GraphOptions;
+
+using DegenerateEdges = GraphOptions::DegenerateEdges;
+using SiblingPairs = GraphOptions::SiblingPairs;
+
+using Edge = Graph::Edge;
+using EdgeId = Graph::EdgeId;
+using VertexId = Graph::VertexId;
+
+namespace s2builderutil {
+
+ClosedSetNormalizer::ClosedSetNormalizer(
+ const Options& options, const vector<GraphOptions>& graph_options_out)
+ : options_(options),
+ graph_options_out_(graph_options_out),
+ graph_options_in_(graph_options_out_),
+ sentinel_(std::numeric_limits<VertexId>::max(),
+ std::numeric_limits<VertexId>::max()) {
+ S2_DCHECK_EQ(graph_options_out_.size(), 3);
+ S2_DCHECK(graph_options_out_[0].edge_type() == EdgeType::DIRECTED);
+ S2_DCHECK(graph_options_out_[2].edge_type() == EdgeType::DIRECTED);
+
+ // NOTE(ericv): Supporting these options would require some extra code in
+ // order to handle undirected edges, and they are not useful for building
+ // polylines anyway (they are intended for polygon meshes).
+ S2_DCHECK(graph_options_out_[1].sibling_pairs() != SiblingPairs::CREATE);
+ S2_DCHECK(graph_options_out_[1].sibling_pairs() != SiblingPairs::REQUIRE);
+
+ // Set the GraphOptions for the input graphs to ensure that (1) they share a
+ // common set of vertices, (2) degenerate edges are kept only if they are
+ // isolated, and (3) multiple copies of siblings pairs are discarded. (Note
+ // that there may be multiple copies of isolated degenerate edges; clients
+ // can eliminate them if desired using DuplicateEdges::MERGE.)
+ for (int dim = 0; dim < 3; ++dim) {
+ graph_options_in_[dim].set_allow_vertex_filtering(false);
+ }
+ graph_options_in_[1].set_degenerate_edges(DegenerateEdges::DISCARD_EXCESS);
+ graph_options_in_[2].set_degenerate_edges(DegenerateEdges::DISCARD_EXCESS);
+ graph_options_in_[2].set_sibling_pairs(SiblingPairs::DISCARD_EXCESS);
+}
+
+const vector<Graph>& ClosedSetNormalizer::Run(
+ const vector<Graph>& g, S2Error* error) {
+ // Ensure that the input graphs were built with our requested options.
+ for (int dim = 0; dim < 3; ++dim) {
+ S2_DCHECK(g[dim].options() == graph_options_in_[dim]);
+ }
+ if (options_.suppress_lower_dimensions()) {
+ // Build the auxiliary data needed to suppress lower-dimensional edges.
+ in_edges2_ = g[2].GetInEdgeIds();
+ is_suppressed_.resize(g[0].vertices().size());
+ for (int dim = 1; dim <= 2; ++dim) {
+ for (int e = 0; e < g[dim].num_edges(); ++e) {
+ Edge edge = g[dim].edge(e);
+ if (edge.first != edge.second) {
+ is_suppressed_[edge.first] = true;
+ is_suppressed_[edge.second] = true;
+ }
+ }
+ }
+ }
+
+ // Compute the edges that belong in the output graphs.
+ NormalizeEdges(g, error);
+
+ // If any edges were added or removed, we need to run Graph::ProcessEdges to
+ // ensure that the edges satisfy the requested GraphOptions. Note that
+ // since edges are never added to dimension 2, we can use the edge count to
+ // test whether any edges were removed. If no edges were removed from
+ // dimension 2, then no edges were added to dimension 1, and so we can again
+ // use the edge count to test whether any edges were removed, etc.
+ bool modified[3];
+ bool any_modified = false;
+ for (int dim = 2; dim >= 0; --dim) {
+ if (new_edges_[dim].size() != g[dim].num_edges()) any_modified = true;
+ modified[dim] = any_modified;
+ }
+ if (!any_modified) {
+ for (int dim = 0; dim < 3; ++dim) {
+ // Copy the graphs to ensure that they have the GraphOptions that were
+ // originally requested.
+ new_graphs_.push_back(Graph(
+ graph_options_out_[dim], &g[dim].vertices(), &g[dim].edges(),
+ &g[dim].input_edge_id_set_ids(), &g[dim].input_edge_id_set_lexicon(),
+ &g[dim].label_set_ids(), &g[dim].label_set_lexicon(),
+ g[dim].is_full_polygon_predicate()));
+ }
+ } else {
+ // Make a copy of input_edge_id_set_lexicon() so that ProcessEdges can
+ // merge edges if necessary.
+ new_input_edge_id_set_lexicon_ = g[0].input_edge_id_set_lexicon();
+ for (int dim = 0; dim < 3; ++dim) {
+ if (modified[dim]) {
+ Graph::ProcessEdges(&graph_options_out_[dim], &new_edges_[dim],
+ &new_input_edge_ids_[dim],
+ &new_input_edge_id_set_lexicon_, error);
+ }
+ new_graphs_.push_back(Graph(
+ graph_options_out_[dim], &g[dim].vertices(), &new_edges_[dim],
+ &new_input_edge_ids_[dim], &new_input_edge_id_set_lexicon_,
+ &g[dim].label_set_ids(), &g[dim].label_set_lexicon(),
+ g[dim].is_full_polygon_predicate()));
+ }
+ }
+ return new_graphs_;
+}
+
+// Helper function that advances to the next edge in the given graph,
+// returning a sentinel value once all edges are exhausted.
+inline Edge ClosedSetNormalizer::Advance(const Graph& g, EdgeId* e) const {
+ return (++*e == g.num_edges()) ? sentinel_ : g.edge(*e);
+}
+
+// Helper function that advances to the next incoming edge in the given graph,
+// returning a sentinel value once all edges are exhausted.
+inline Edge ClosedSetNormalizer::AdvanceIncoming(
+ const Graph& g, const vector<EdgeId>& in_edges, int* i) const {
+ return ((++*i == in_edges.size()) ? sentinel_ :
+ Graph::reverse(g.edge(in_edges[*i])));
+ }
+
+void ClosedSetNormalizer::NormalizeEdges(const vector<Graph>& g,
+ S2Error* error) {
+ // Find the degenerate polygon edges and sibling pairs, and classify each
+ // edge as belonging to either a shell or a hole.
+ auto degeneracies = FindPolygonDegeneracies(g[2], error);
+ auto degeneracy = degeneracies.begin();
+
+ // Walk through the three edge vectors performing a merge join. We also
+ // maintain positions in two other auxiliary vectors: the vector of sorted
+ // polygon degeneracies (degeneracies), and the vector of incoming polygon
+ // edges (if we are suppressing lower-dimensional duplicate edges).
+ EdgeId e0 = -1, e1 = -1, e2 = -1; // Current position in g[dim].edges()
+ int in_e2 = -1; // Current position in in_edges2_
+ Edge edge0 = Advance(g[0], &e0);
+ Edge edge1 = Advance(g[1], &e1);
+ Edge edge2 = Advance(g[2], &e2);
+ Edge in_edge2 = AdvanceIncoming(g[2], in_edges2_, &in_e2);
+ for (;;) {
+ if (edge2 <= edge1 && edge2 <= edge0) {
+ if (edge2 == sentinel_) break;
+ if (degeneracy == degeneracies.end() || degeneracy->edge_id != e2) {
+ // Normal polygon edge (not part of a degeneracy).
+ AddEdge(2, g[2], e2);
+ while (options_.suppress_lower_dimensions() && edge1 == edge2) {
+ edge1 = Advance(g[1], &e1);
+ }
+ } else if (!(degeneracy++)->is_hole) {
+ // Edge belongs to a degenerate shell.
+ if (edge2.first != edge2.second) {
+ AddEdge(1, g[2], e2);
+ // Since this edge was demoted, make sure that it does not suppress
+ // any coincident polyline edge(s).
+ while (edge1 == edge2) {
+ AddEdge(1, g[1], e1);
+ edge1 = Advance(g[1], &e1);
+ }
+ } else {
+ // The test below is necessary because a single-vertex polygon shell
+ // can be discarded by a polyline edge incident to that vertex.
+ if (!is_suppressed(edge2.first)) AddEdge(0, g[2], e2);
+ }
+ }
+ edge2 = Advance(g[2], &e2);
+ } else if (edge1 <= edge0) {
+ if (edge1.first != edge1.second) {
+ // Non-degenerate polyline edge. (Note that in_edges2_ is empty
+ // whenever "suppress_lower_dimensions" is false.)
+ while (in_edge2 < edge1) {
+ in_edge2 = AdvanceIncoming(g[2], in_edges2_, &in_e2);
+ }
+ if (edge1 != in_edge2) AddEdge(1, g[1], e1);
+ } else {
+ // Degenerate polyline edge.
+ if (!is_suppressed(edge1.first)) AddEdge(0, g[1], e1);
+ if (g[1].options().edge_type() == EdgeType::UNDIRECTED) ++e1;
+ }
+ edge1 = Advance(g[1], &e1);
+ } else {
+ // Input point.
+ if (!is_suppressed(edge0.first)) AddEdge(0, g[0], e0);
+ edge0 = Advance(g[0], &e0);
+ }
+ }
+}
+
+inline void ClosedSetNormalizer::AddEdge(int new_dim, const Graph& g,
+ EdgeId e) {
+ new_edges_[new_dim].push_back(g.edge(e));
+ new_input_edge_ids_[new_dim].push_back(g.input_edge_id_set_id(e));
+}
+
+inline bool ClosedSetNormalizer::is_suppressed(VertexId v) const {
+ return options_.suppress_lower_dimensions() && is_suppressed_[v];
+}
+
+// This method implements the NormalizeClosedSet function. The Create()
+// method allocates a single object of this class whose ownership is shared
+// (using shared_ptr) among the three returned S2Builder::Layers. Here is how
+// the process works:
+//
+// - The returned layers are passed to a class (such as S2Builder or
+// S2BooleanOperation) that calls their Build methods. We call these the
+// "input layers" because they provide the input to ClosedSetNormalizer.
+//
+// - When Build() is called on the first two layers, pointers to the
+// corresponding Graph arguments are saved.
+//
+// - When Build() is called on the third layer, ClosedSetNormalizer is used
+// to normalize the graphs, and then the Build() method of each of the
+// three output layers is called.
+//
+// TODO(ericv): Consider generalizing this technique as a public class.
+class NormalizeClosedSetImpl {
+ public:
+ static LayerVector Create(LayerVector output_layers,
+ const ClosedSetNormalizer::Options& options) {
+ using Impl = NormalizeClosedSetImpl;
+ shared_ptr<Impl> impl(new Impl(std::move(output_layers), options));
+ LayerVector result;
+ for (int dim = 0; dim < 3; ++dim) {
+ result.push_back(make_unique<DimensionLayer>(
+ dim, impl->normalizer_.graph_options()[dim], impl));
+ }
+ return result;
+ }
+
+ private:
+ NormalizeClosedSetImpl(LayerVector output_layers,
+ const ClosedSetNormalizer::Options& options)
+ : output_layers_(std::move(output_layers)),
+ normalizer_(options, vector<GraphOptions>{
+ output_layers_[0]->graph_options(),
+ output_layers_[1]->graph_options(),
+ output_layers_[2]->graph_options()}),
+ graphs_(3), graphs_left_(3) {
+ S2_DCHECK_EQ(3, output_layers_.size());
+ }
+
+ class DimensionLayer : public S2Builder::Layer {
+ public:
+ DimensionLayer(int dimension, const GraphOptions& graph_options,
+ shared_ptr<NormalizeClosedSetImpl> impl)
+ : dimension_(dimension), graph_options_(graph_options),
+ impl_(std::move(impl)) {}
+
+ GraphOptions graph_options() const override { return graph_options_; }
+
+ void Build(const Graph& g, S2Error* error) override {
+ impl_->Build(dimension_, g, error);
+ }
+
+ private:
+ int dimension_;
+ GraphOptions graph_options_;
+ shared_ptr<NormalizeClosedSetImpl> impl_;
+ };
+
+ void Build(int dimension, const Graph& g, S2Error* error) {
+ // Errors are reported only on the last layer built.
+ graphs_[dimension] = g;
+ if (--graphs_left_ > 0) return;
+
+ vector<Graph> output = normalizer_.Run(graphs_, error);
+ for (int dim = 0; dim < 3; ++dim) {
+ output_layers_[dim]->Build(output[dim], error);
+ }
+ }
+
+ private:
+ vector<unique_ptr<S2Builder::Layer>> output_layers_;
+ ClosedSetNormalizer normalizer_;
+ vector<Graph> graphs_;
+ int graphs_left_;
+};
+
+LayerVector NormalizeClosedSet(LayerVector output_layers,
+ const ClosedSetNormalizer::Options& options) {
+ return NormalizeClosedSetImpl::Create(std::move(output_layers), options);
+}
+
+} // namespace s2builderutil
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2builderutil_find_polygon_degeneracies.h"
+
+#include <cstdlib>
+#include <utility>
+#include <vector>
+
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/mutable_s2shape_index.h"
+#include "s2/s2builder_graph.h"
+#include "s2/s2builderutil_graph_shape.h"
+#include "s2/s2contains_vertex_query.h"
+#include "s2/s2crossing_edge_query.h"
+#include "s2/s2edge_crosser.h"
+#include "s2/s2pointutil.h"
+#include "s2/s2predicates.h"
+
+using absl::make_unique;
+using std::make_pair;
+using std::pair;
+using std::vector;
+
+using EdgeType = S2Builder::EdgeType;
+using Graph = S2Builder::Graph;
+using GraphOptions = S2Builder::GraphOptions;
+
+using Edge = Graph::Edge;
+using EdgeId = Graph::EdgeId;
+using VertexId = Graph::VertexId;
+
+using DegenerateEdges = GraphOptions::DegenerateEdges;
+using SiblingPairs = GraphOptions::SiblingPairs;
+
+using ShapeEdgeId = s2shapeutil::ShapeEdgeId;
+
+namespace s2builderutil {
+
+namespace {
+
+// The algorithm builds a set of connected components containing all edges
+// that form degeneracies. The shell/hole status of each degeneracy is
+// initially unknown, and is expressed relative to the root vertex: "is_hole"
+// means that the degeneracy is a hole if and only if the root vertex turns
+// out to be inside the polygon.
+struct Component {
+ // The root vertex from which this component was built.
+ VertexId root;
+
+ // +1 if "root" inside the polygon, -1 if outside, and 0 if unknown.
+ int root_sign = 0;
+
+ // The degeneracies found in this component. "is_hole" is expressed
+ // relative to the root vertex: the degeneracy is a hole iff the root vertex
+ // turns out to be inside the polygon (i.e., root_sign > 0).
+ vector<PolygonDegeneracy> degeneracies;
+};
+
+// The actual implementation of FindPolygonDegeneracies.
+class DegeneracyFinder {
+ public:
+ explicit DegeneracyFinder(const S2Builder::Graph* g)
+ : g_(*g), in_(g_), out_(g_) {
+ }
+ vector<PolygonDegeneracy> Run(S2Error* error);
+
+ private:
+ // Methods are documented below.
+ int ComputeDegeneracies();
+ Component BuildComponent(VertexId root);
+ bool CrossingParity(VertexId v0, VertexId v1, bool include_same) const;
+ VertexId FindUnbalancedVertex() const;
+ int ContainsVertexSign(VertexId v0) const;
+ void ComputeUnknownSignsBruteForce(VertexId known_vertex,
+ int known_vertex_sign,
+ vector<Component>* components) const;
+ void ComputeUnknownSignsIndexed(VertexId known_vertex, int known_vertex_sign,
+ vector<Component>* components) const;
+ vector<PolygonDegeneracy> MergeDegeneracies(
+ const vector<Component>& components) const;
+
+ const Graph& g_;
+ Graph::VertexInMap in_;
+ Graph::VertexOutMap out_;
+ vector<bool> is_vertex_used_; // Has vertex been visited?
+ vector<bool> is_edge_degeneracy_; // Belongs to a degeneracy?
+ vector<bool> is_vertex_unbalanced_; // Has unbalanced sibling pairs?
+};
+
+vector<PolygonDegeneracy> DegeneracyFinder::Run(S2Error* error) {
+ // Mark all degenerate edges and sibling pairs in the "is_edge_degeneracy_"
+ // vector, and mark any vertices with unbalanced edges in the
+ // "is_vertex_unbalanced_" vector.
+ int num_degeneracies = ComputeDegeneracies();
+ if (num_degeneracies == 0) return {};
+
+ // If all edges are degenerate, then use IsFullPolygon() to classify the
+ // degeneracies (they are necessarily all the same type).
+ if (num_degeneracies == g_.num_edges()) {
+ bool is_hole = g_.IsFullPolygon(error);
+ vector<PolygonDegeneracy> result(g_.num_edges());
+ for (int e = 0; e < g_.num_edges(); ++e) {
+ result[e] = PolygonDegeneracy(e, is_hole);
+ }
+ return result;
+ }
+
+ // Otherwise repeatedly build components starting from an unvisited
+ // degeneracy. (This avoids building components that don't contain any
+ // degeneracies.) Each component records the "is_hole" status of each
+ // degeneracy relative to the root vertex of that component. If the
+ // component contains any non-degenerate portions, then we also determine
+ // whether the root vertex is contained by the component (root_sign).
+ // In addition we keep track of the number of components that were
+ // completely degenerate (to help us decide whether to build an index).
+ vector<Component> components;
+ VertexId known_vertex = -1;
+ int known_vertex_sign = 0;
+ int num_unknown_signs = 0;
+ is_vertex_used_.resize(g_.num_vertices());
+ for (int e = 0; e < g_.num_edges(); ++e) {
+ if (is_edge_degeneracy_[e]) {
+ VertexId root = g_.edge(e).first;
+ if (is_vertex_used_[root]) continue;
+ Component component = BuildComponent(root);
+ if (component.root_sign == 0) {
+ ++num_unknown_signs;
+ } else {
+ known_vertex = root;
+ known_vertex_sign = component.root_sign;
+ }
+ components.push_back(component);
+ }
+ }
+
+ // If some components have an unknown root_sign (i.e., it is unknown whether
+ // the root vertex is contained by the polygon or not), we determine the
+ // sign of those root vertices by counting crossings starting from a vertex
+ // whose sign is known. Depending on how many components we need to do this
+ // for, it may be worthwhile to build an index first.
+ if (num_unknown_signs > 0) {
+ if (known_vertex_sign == 0) {
+ known_vertex = FindUnbalancedVertex();
+ known_vertex_sign = ContainsVertexSign(known_vertex);
+ }
+ const int kMaxUnindexedContainsCalls = 20; // Tuned using benchmarks.
+ if (num_unknown_signs <= kMaxUnindexedContainsCalls) {
+ ComputeUnknownSignsBruteForce(known_vertex, known_vertex_sign,
+ &components);
+ } else {
+ ComputeUnknownSignsIndexed(known_vertex, known_vertex_sign,
+ &components);
+ }
+ }
+ // Finally we convert the "is_hole" status of each degeneracy from a
+ // relative value (compared to the component's root vertex) to an absolute
+ // one, and sort all the degeneracies by EdgeId.
+ return MergeDegeneracies(components);
+}
+
+int DegeneracyFinder::ComputeDegeneracies() {
+ is_edge_degeneracy_.resize(g_.num_edges());
+ is_vertex_unbalanced_.resize(g_.num_vertices());
+ int num_degeneracies = 0;
+ const vector<EdgeId>& in_edge_ids = in_.in_edge_ids();
+ int n = g_.num_edges();
+ for (int in = 0, out = 0; out < n; ++out) {
+ Edge out_edge = g_.edge(out);
+ if (out_edge.first == out_edge.second) {
+ is_edge_degeneracy_[out] = true;
+ ++num_degeneracies;
+ } else {
+ while (in < n && Graph::reverse(g_.edge(in_edge_ids[in])) < out_edge) {
+ ++in;
+ }
+ if (in < n && Graph::reverse(g_.edge(in_edge_ids[in])) == out_edge) {
+ is_edge_degeneracy_[out] = true;
+ ++num_degeneracies;
+ } else {
+ // This edge does not have a sibling, which mean that we can determine
+ // whether either vertex is contained by the polygon (using semi-open
+ // boundaries) by examining only the edges incident to that vertex.
+ // We only mark the first vertex since there is no advantage to
+ // finding more than one unbalanced vertex per connected component.
+ is_vertex_unbalanced_[out_edge.first] = true;
+ }
+ }
+ }
+ return num_degeneracies;
+}
+
+// Build a connected component starting at the given root vertex. The
+// information returned includes: the root vertex, whether the containment
+// status of the root vertex could be determined using only the edges in this
+// component, and a vector of the edges that belong to degeneracies along with
+// the shell/hole status of each such edge relative to the root vertex.
+Component DegeneracyFinder::BuildComponent(VertexId root) {
+ Component result;
+ result.root = root;
+ // We keep track of the frontier of unexplored vertices, and whether each
+ // vertex is on the same side of the polygon boundary as the root vertex.
+ vector<pair<VertexId, bool>> frontier;
+ frontier.push_back(make_pair(root, true));
+ is_vertex_used_[root] = true;
+ while (!frontier.empty()) {
+ VertexId v0 = frontier.back().first;
+ bool v0_same_inside = frontier.back().second; // Same as root vertex?
+ frontier.pop_back();
+ if (result.root_sign == 0 && is_vertex_unbalanced_[v0]) {
+ int v0_sign = ContainsVertexSign(v0);
+ S2_DCHECK_NE(v0_sign, 0);
+ result.root_sign = v0_same_inside ? v0_sign : -v0_sign;
+ }
+ for (EdgeId e : out_.edge_ids(v0)) {
+ VertexId v1 = g_.edge(e).second;
+ bool same_inside = v0_same_inside ^ CrossingParity(v0, v1, false);
+ if (is_edge_degeneracy_[e]) {
+ result.degeneracies.push_back(PolygonDegeneracy(e, same_inside));
+ }
+ if (is_vertex_used_[v1]) continue;
+ same_inside ^= CrossingParity(v1, v0, true);
+ frontier.push_back(make_pair(v1, same_inside));
+ is_vertex_used_[v1] = true;
+ }
+ }
+ return result;
+}
+
+// Counts the number of times that (v0, v1) crosses the edges incident to v0,
+// and returns the result modulo 2. This is equivalent to calling
+// S2::VertexCrossing for the edges incident to v0, except that this
+// implementation is more efficient (since it doesn't need to determine which
+// two edge vertices are the same).
+//
+// If "include_same" is false, then the edge (v0, v1) and its sibling (v1, v0)
+// (if any) are excluded from the parity calculation.
+bool DegeneracyFinder::CrossingParity(VertexId v0, VertexId v1,
+ bool include_same) const {
+ int crossings = 0;
+ S2Point p0 = g_.vertex(v0);
+ S2Point p1 = g_.vertex(v1);
+ S2Point p0_ref = S2::Ortho(p0);
+ for (const Edge& edge : out_.edges(v0)) {
+ if (edge.second == v1) {
+ if (include_same) ++crossings;
+ } else if (s2pred::OrderedCCW(p0_ref, g_.vertex(edge.second), p1, p0)) {
+ ++crossings;
+ }
+ }
+ for (EdgeId e : in_.edge_ids(v0)) {
+ Edge edge = g_.edge(e);
+ if (edge.first == v1) {
+ if (include_same) ++crossings;
+ } else if (s2pred::OrderedCCW(p0_ref, g_.vertex(edge.first), p1, p0)) {
+ ++crossings;
+ }
+ }
+ return crossings & 1;
+}
+
+VertexId DegeneracyFinder::FindUnbalancedVertex() const {
+ for (VertexId v = 0; v < g_.num_vertices(); ++v) {
+ if (is_vertex_unbalanced_[v]) return v;
+ }
+ S2_LOG(DFATAL) << "Could not find previously marked unbalanced vertex";
+ return -1;
+}
+
+int DegeneracyFinder::ContainsVertexSign(VertexId v0) const {
+ S2ContainsVertexQuery query(g_.vertex(v0));
+ for (const Edge& edge : out_.edges(v0)) {
+ query.AddEdge(g_.vertex(edge.second), 1);
+ }
+ for (EdgeId e : in_.edge_ids(v0)) {
+ query.AddEdge(g_.vertex(g_.edge(e).first), -1);
+ }
+ return query.ContainsSign();
+}
+
+// Determines any unknown signs of component root vertices by counting
+// crossings starting from a vertex whose sign is known. This version simply
+// tests all edges for crossings.
+void DegeneracyFinder::ComputeUnknownSignsBruteForce(
+ VertexId known_vertex, int known_vertex_sign,
+ vector<Component>* components) const {
+ S2EdgeCrosser crosser;
+ for (Component& component : *components) {
+ if (component.root_sign != 0) continue;
+ bool inside = known_vertex_sign > 0;
+ crosser.Init(&g_.vertex(known_vertex), &g_.vertex(component.root));
+ for (EdgeId e = 0; e < g_.num_edges(); ++e) {
+ if (is_edge_degeneracy_[e]) continue;
+ const Edge& edge = g_.edge(e);
+ inside ^= crosser.EdgeOrVertexCrossing(&g_.vertex(edge.first),
+ &g_.vertex(edge.second));
+ }
+ component.root_sign = inside ? 1 : -1;
+ }
+}
+
+// Like ComputeUnknownSignsBruteForce, except that this method uses an index
+// to find the set of edges that cross a given edge.
+void DegeneracyFinder::ComputeUnknownSignsIndexed(
+ VertexId known_vertex, int known_vertex_sign,
+ vector<Component>* components) const {
+ MutableS2ShapeIndex index;
+ index.Add(make_unique<GraphShape>(&g_));
+ S2CrossingEdgeQuery query(&index);
+ vector<ShapeEdgeId> crossing_edges;
+ S2EdgeCrosser crosser;
+ for (Component& component : *components) {
+ if (component.root_sign != 0) continue;
+ bool inside = known_vertex_sign > 0;
+ crosser.Init(&g_.vertex(known_vertex), &g_.vertex(component.root));
+ query.GetCandidates(g_.vertex(known_vertex), g_.vertex(component.root),
+ *index.shape(0), &crossing_edges);
+ for (ShapeEdgeId id : crossing_edges) {
+ int e = id.edge_id;
+ if (is_edge_degeneracy_[e]) continue;
+ inside ^= crosser.EdgeOrVertexCrossing(&g_.vertex(g_.edge(e).first),
+ &g_.vertex(g_.edge(e).second));
+ }
+ component.root_sign = inside ? 1 : -1;
+ }
+}
+
+// Merges the degeneracies from all components together, and computes the
+// final "is_hole" status of each edge (since up to this point, the "is_hole"
+// value has been expressed relative to the root vertex of each component).
+vector<PolygonDegeneracy> DegeneracyFinder::MergeDegeneracies(
+ const vector<Component>& components) const {
+ vector<PolygonDegeneracy> result;
+ for (const Component& component : components) {
+ S2_DCHECK_NE(component.root_sign, 0);
+ bool invert = component.root_sign < 0;
+ for (const auto& d : component.degeneracies) {
+ result.push_back(PolygonDegeneracy(d.edge_id, d.is_hole ^ invert));
+ }
+ }
+ std::sort(result.begin(), result.end());
+ return result;
+}
+
+void CheckGraphOptions(const Graph& g) {
+ S2_DCHECK(g.options().edge_type() == EdgeType::DIRECTED);
+ S2_DCHECK(g.options().degenerate_edges() == DegenerateEdges::DISCARD ||
+ g.options().degenerate_edges() == DegenerateEdges::DISCARD_EXCESS);
+ S2_DCHECK(g.options().sibling_pairs() == SiblingPairs::DISCARD ||
+ g.options().sibling_pairs() == SiblingPairs::DISCARD_EXCESS);
+}
+
+} // namespace
+
+vector<PolygonDegeneracy> FindPolygonDegeneracies(const Graph& g,
+ S2Error* error) {
+ CheckGraphOptions(g);
+ if (g.options().degenerate_edges() == DegenerateEdges::DISCARD &&
+ g.options().sibling_pairs() == SiblingPairs::DISCARD) {
+ return {}; // All degeneracies have already been discarded.
+ }
+ return DegeneracyFinder(&g).Run(error);
+}
+
+bool IsFullyDegenerate(const S2Builder::Graph& g) {
+ CheckGraphOptions(g);
+ const vector<Edge>& edges = g.edges();
+ for (int e = 0; e < g.num_edges(); ++e) {
+ Edge edge = edges[e];
+ if (edge.first == edge.second) continue;
+ if (!std::binary_search(edges.begin(), edges.end(), Graph::reverse(edge))) {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace s2builderutil
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2builderutil_lax_polygon_layer.h"
+
+#include <algorithm>
+#include <memory>
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/s2builderutil_find_polygon_degeneracies.h"
+#include "s2/s2debug.h"
+
+using std::vector;
+
+using EdgeType = S2Builder::EdgeType;
+using Graph = S2Builder::Graph;
+using GraphOptions = S2Builder::GraphOptions;
+using Label = S2Builder::Label;
+
+using DegenerateEdges = GraphOptions::DegenerateEdges;
+using DuplicateEdges = GraphOptions::DuplicateEdges;
+using SiblingPairs = GraphOptions::SiblingPairs;
+
+using Edge = Graph::Edge;
+using EdgeId = Graph::EdgeId;
+using InputEdgeIdSetId = Graph::InputEdgeIdSetId;
+using LoopType = Graph::LoopType;
+
+namespace s2builderutil {
+
+using DegenerateBoundaries = LaxPolygonLayer::Options::DegenerateBoundaries;
+
+LaxPolygonLayer::LaxPolygonLayer(S2LaxPolygonShape* polygon,
+ const Options& options) {
+ Init(polygon, nullptr, nullptr, options);
+}
+
+LaxPolygonLayer::LaxPolygonLayer(
+ S2LaxPolygonShape* polygon, LabelSetIds* label_set_ids,
+ IdSetLexicon* label_set_lexicon, const Options& options) {
+ Init(polygon, label_set_ids, label_set_lexicon, options);
+}
+
+void LaxPolygonLayer::Init(
+ S2LaxPolygonShape* polygon, LabelSetIds* label_set_ids,
+ IdSetLexicon* label_set_lexicon, const Options& options) {
+ S2_DCHECK_EQ(label_set_ids == nullptr, label_set_lexicon == nullptr);
+ polygon_ = polygon;
+ label_set_ids_ = label_set_ids;
+ label_set_lexicon_ = label_set_lexicon;
+ options_ = options;
+}
+
+GraphOptions LaxPolygonLayer::graph_options() const {
+ if (options_.degenerate_boundaries() == DegenerateBoundaries::DISCARD) {
+ // There should not be any duplicate edges, but if there are then we keep
+ // them since this yields more comprehensible error messages.
+ return GraphOptions(options_.edge_type(), DegenerateEdges::DISCARD,
+ DuplicateEdges::KEEP, SiblingPairs::DISCARD);
+ } else {
+ // Keep at most one copy of each sibling pair and each isolated vertex.
+ // We need DuplicateEdges::MERGE because DegenerateEdges::DISCARD_EXCESS
+ // can still keep multiple copies (it only discards degenerate edges that
+ // are connected to non-degenerate edges).
+ return GraphOptions(options_.edge_type(), DegenerateEdges::DISCARD_EXCESS,
+ DuplicateEdges::MERGE, SiblingPairs::DISCARD_EXCESS);
+ }
+}
+
+void LaxPolygonLayer::AppendPolygonLoops(
+ const Graph& g, const vector<Graph::EdgeLoop>& edge_loops,
+ vector<vector<S2Point>>* loops) const {
+ for (const auto& edge_loop : edge_loops) {
+ vector<S2Point> vertices;
+ vertices.reserve(edge_loop.size());
+ for (auto edge_id : edge_loop) {
+ vertices.push_back(g.vertex(g.edge(edge_id).first));
+ }
+ loops->push_back(std::move(vertices));
+ }
+}
+
+void LaxPolygonLayer::AppendEdgeLabels(
+ const Graph& g,
+ const vector<Graph::EdgeLoop>& edge_loops) {
+ if (!label_set_ids_) return;
+
+ vector<Label> labels; // Temporary storage for labels.
+ Graph::LabelFetcher fetcher(g, options_.edge_type());
+ for (const auto& edge_loop : edge_loops) {
+ vector<LabelSetId> loop_label_set_ids;
+ loop_label_set_ids.reserve(edge_loop.size());
+ for (auto edge_id : edge_loop) {
+ fetcher.Fetch(edge_id, &labels);
+ loop_label_set_ids.push_back(label_set_lexicon_->Add(labels));
+ }
+ label_set_ids_->push_back(std::move(loop_label_set_ids));
+ }
+}
+
+// Returns all edges of "g" except for those identified by "edges_to_discard".
+static void DiscardEdges(const Graph& g, const vector<EdgeId>& edges_to_discard,
+ vector<Edge>* new_edges,
+ vector<InputEdgeIdSetId>* new_input_edge_id_set_ids) {
+ S2_DCHECK(std::is_sorted(edges_to_discard.begin(), edges_to_discard.end()));
+ new_edges->clear();
+ new_input_edge_id_set_ids->clear();
+ new_edges->reserve(g.num_edges());
+ new_input_edge_id_set_ids->reserve(g.num_edges());
+ auto it = edges_to_discard.begin();
+ for (int e = 0; e < g.num_edges(); ++e) {
+ if (it != edges_to_discard.end() && e == *it) {
+ ++it;
+ } else {
+ new_edges->push_back(g.edge(e));
+ new_input_edge_id_set_ids->push_back(g.input_edge_id_set_id(e));
+ }
+ }
+ S2_DCHECK(it == edges_to_discard.end());
+}
+
+static void MaybeAddFullLoop(
+ const Graph& g, vector<vector<S2Point>>* loops, S2Error* error) {
+ if (g.IsFullPolygon(error)) {
+ loops->push_back({}); // Full loop.
+ }
+}
+void LaxPolygonLayer::BuildDirected(Graph g, S2Error* error) {
+ // Some cases are implemented by constructing a new graph with certain
+ // degenerate edges removed (overwriting "g"). "new_edges" is where the
+ // edges for the new graph are stored.
+ vector<Edge> new_edges;
+ vector<InputEdgeIdSetId> new_input_edge_id_set_ids;
+ vector<vector<S2Point>> loops;
+ auto degenerate_boundaries = options_.degenerate_boundaries();
+ if (degenerate_boundaries == DegenerateBoundaries::DISCARD) {
+ // This is the easiest case, since there are no degeneracies.
+ if (g.num_edges() == 0) MaybeAddFullLoop(g, &loops, error);
+ } else if (degenerate_boundaries == DegenerateBoundaries::KEEP) {
+ // S2LaxPolygonShape doesn't need to distinguish degenerate shells from
+ // holes except when the entire graph is degenerate, in which case we need
+ // to decide whether it represents an empty polygons possibly with
+ // degenerate shells, or a full polygon possibly with degenerate holes.
+ if (s2builderutil::IsFullyDegenerate(g)) {
+ MaybeAddFullLoop(g, &loops, error);
+ }
+ } else {
+ // For DISCARD_SHELLS and DISCARD_HOLES we first determine whether any
+ // degenerate loops of the given type exist, and if so we construct a new
+ // graph with those edges removed (overwriting "g").
+ bool discard_holes =
+ (degenerate_boundaries == DegenerateBoundaries::DISCARD_HOLES);
+ auto degeneracies = s2builderutil::FindPolygonDegeneracies(g, error);
+ if (!error->ok()) return;
+ if (degeneracies.size() == g.num_edges()) {
+ if (degeneracies.empty()) {
+ MaybeAddFullLoop(g, &loops, error);
+ } else if (degeneracies[0].is_hole) {
+ loops.push_back({}); // Full loop.
+ }
+ }
+ vector<EdgeId> edges_to_discard;
+ for (auto degeneracy : degeneracies) {
+ if (degeneracy.is_hole == discard_holes) {
+ edges_to_discard.push_back(degeneracy.edge_id);
+ }
+ }
+ if (!edges_to_discard.empty()) {
+ // Construct a new graph that discards the unwanted edges.
+ std::sort(edges_to_discard.begin(), edges_to_discard.end());
+ DiscardEdges(g, edges_to_discard, &new_edges, &new_input_edge_id_set_ids);
+ g = Graph(g.options(), &g.vertices(),
+ &new_edges, &new_input_edge_id_set_ids,
+ &g.input_edge_id_set_lexicon(), &g.label_set_ids(),
+ &g.label_set_lexicon(), g.is_full_polygon_predicate());
+ }
+ }
+ vector<Graph::EdgeLoop> edge_loops;
+ if (!g.GetDirectedLoops(LoopType::CIRCUIT, &edge_loops, error)) {
+ return;
+ }
+ AppendPolygonLoops(g, edge_loops, &loops);
+ AppendEdgeLabels(g, edge_loops);
+ vector<Graph::EdgeLoop>().swap(edge_loops); // Release memory
+ vector<Edge>().swap(new_edges);
+ vector<InputEdgeIdSetId>().swap(new_input_edge_id_set_ids);
+ polygon_->Init(loops);
+}
+
+void LaxPolygonLayer::Build(const Graph& g, S2Error* error) {
+ if (label_set_ids_) label_set_ids_->clear();
+ if (g.options().edge_type() == EdgeType::DIRECTED) {
+ BuildDirected(g, error);
+ } else {
+ error->Init(S2Error::UNIMPLEMENTED, "Undirected edges not supported yet");
+ }
+}
+
+} // namespace s2builderutil
--- /dev/null
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2builderutil_s2point_vector_layer.h"
+
+#include "s2/s2builder_graph.h"
+
+using std::vector;
+
+using EdgeType = S2Builder::EdgeType;
+using Graph = S2Builder::Graph;
+using GraphOptions = S2Builder::GraphOptions;
+using Label = S2Builder::Label;
+
+using DegenerateEdges = GraphOptions::DegenerateEdges;
+using DuplicateEdges = GraphOptions::DuplicateEdges;
+using SiblingPairs = GraphOptions::SiblingPairs;
+
+using EdgeId = Graph::EdgeId;
+
+namespace s2builderutil {
+
+S2PointVectorLayer::S2PointVectorLayer(vector<S2Point>* points,
+ const Options& options)
+ : S2PointVectorLayer(points, nullptr, nullptr, options) {}
+
+S2PointVectorLayer::S2PointVectorLayer(vector<S2Point>* points,
+ LabelSetIds* label_set_ids,
+ IdSetLexicon* label_set_lexicon,
+ const Options& options)
+ : points_(points),
+ label_set_ids_(label_set_ids),
+ label_set_lexicon_(label_set_lexicon),
+ options_(options) {}
+
+void S2PointVectorLayer::Build(const Graph& g, S2Error* error) {
+ Graph::LabelFetcher fetcher(g, EdgeType::DIRECTED);
+
+ vector<Label> labels; // Temporary storage for labels.
+ for (EdgeId edge_id = 0; edge_id < g.edges().size(); edge_id++) {
+ auto& edge = g.edge(edge_id);
+ if (edge.first != edge.second) {
+ error->Init(S2Error::INVALID_ARGUMENT, "Found non-degenerate edges");
+ continue;
+ }
+ points_->push_back(g.vertex(edge.first));
+ if (label_set_ids_) {
+ fetcher.Fetch(edge_id, &labels);
+ int set_id = label_set_lexicon_->Add(labels);
+ label_set_ids_->push_back(set_id);
+ }
+ }
+}
+
+GraphOptions S2PointVectorLayer::graph_options() const {
+ return GraphOptions(EdgeType::DIRECTED, DegenerateEdges::KEEP,
+ options_.duplicate_edges(), SiblingPairs::KEEP);
+}
+
+} // namespace s2builderutil
--- /dev/null
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2builderutil_s2polygon_layer.h"
+
+#include <algorithm>
+#include <memory>
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/s2debug.h"
+
+using absl::make_unique;
+using std::make_pair;
+using std::pair;
+using std::unique_ptr;
+using std::vector;
+
+using EdgeType = S2Builder::EdgeType;
+using Graph = S2Builder::Graph;
+using GraphOptions = S2Builder::GraphOptions;
+using Label = S2Builder::Label;
+
+using DegenerateEdges = GraphOptions::DegenerateEdges;
+using DuplicateEdges = GraphOptions::DuplicateEdges;
+using SiblingPairs = GraphOptions::SiblingPairs;
+
+using LoopType = Graph::LoopType;
+
+namespace s2builderutil {
+
+S2PolygonLayer::S2PolygonLayer(S2Polygon* polygon, const Options& options) {
+ Init(polygon, nullptr, nullptr, options);
+}
+
+S2PolygonLayer::S2PolygonLayer(
+ S2Polygon* polygon, LabelSetIds* label_set_ids,
+ IdSetLexicon* label_set_lexicon, const Options& options) {
+ Init(polygon, label_set_ids, label_set_lexicon, options);
+}
+
+void S2PolygonLayer::Init(
+ S2Polygon* polygon, LabelSetIds* label_set_ids,
+ IdSetLexicon* label_set_lexicon, const Options& options) {
+ S2_DCHECK_EQ(label_set_ids == nullptr, label_set_lexicon == nullptr);
+ polygon_ = polygon;
+ label_set_ids_ = label_set_ids;
+ label_set_lexicon_ = label_set_lexicon;
+ options_ = options;
+
+ if (options_.validate()) {
+ polygon_->set_s2debug_override(S2Debug::DISABLE);
+ }
+}
+
+GraphOptions S2PolygonLayer::graph_options() const {
+ // Prevent degenerate edges and sibling edge pairs. There should not be any
+ // duplicate edges if the input is valid, but if there are then we keep them
+ // since this tends to produce more comprehensible errors.
+ return GraphOptions(options_.edge_type(), DegenerateEdges::DISCARD,
+ DuplicateEdges::KEEP, SiblingPairs::DISCARD);
+}
+
+void S2PolygonLayer::AppendS2Loops(const Graph& g,
+ const vector<Graph::EdgeLoop>& edge_loops,
+ vector<unique_ptr<S2Loop>>* loops) const {
+ vector<S2Point> vertices;
+ for (const auto& edge_loop : edge_loops) {
+ vertices.reserve(edge_loop.size());
+ for (auto edge_id : edge_loop) {
+ vertices.push_back(g.vertex(g.edge(edge_id).first));
+ }
+ loops->push_back(
+ make_unique<S2Loop>(vertices, polygon_->s2debug_override()));
+ vertices.clear();
+ }
+}
+
+void S2PolygonLayer::AppendEdgeLabels(
+ const Graph& g,
+ const vector<Graph::EdgeLoop>& edge_loops) {
+ if (!label_set_ids_) return;
+
+ vector<Label> labels; // Temporary storage for labels.
+ Graph::LabelFetcher fetcher(g, options_.edge_type());
+ for (const auto& edge_loop : edge_loops) {
+ vector<LabelSetId> loop_label_set_ids;
+ loop_label_set_ids.reserve(edge_loop.size());
+ for (auto edge_id : edge_loop) {
+ fetcher.Fetch(edge_id, &labels);
+ loop_label_set_ids.push_back(label_set_lexicon_->Add(labels));
+ }
+ label_set_ids_->push_back(std::move(loop_label_set_ids));
+ }
+}
+
+void S2PolygonLayer::InitLoopMap(const vector<unique_ptr<S2Loop>>& loops,
+ LoopMap* loop_map) const {
+ if (!label_set_ids_) return;
+ for (const auto& loop : loops) {
+ (*loop_map)[&*loop] =
+ make_pair(&loop - &loops[0], loop->contains_origin());
+ }
+}
+
+void S2PolygonLayer::ReorderEdgeLabels(const LoopMap& loop_map) {
+ if (!label_set_ids_) return;
+ LabelSetIds new_ids(label_set_ids_->size());
+ for (int i = 0; i < polygon_->num_loops(); ++i) {
+ S2Loop* loop = polygon_->loop(i);
+ const pair<int, bool>& old = loop_map.find(loop)->second;
+ new_ids[i].swap((*label_set_ids_)[old.first]);
+ if (loop->contains_origin() != old.second) {
+ // S2Loop::Invert() reverses the order of the vertices, which leaves
+ // the last edge unchanged. For example, the loop ABCD (with edges
+ // AB, BC, CD, DA) becomes the loop DCBA (with edges DC, CB, BA, AD).
+ std::reverse(new_ids[i].begin(), new_ids[i].end() - 1);
+ }
+ }
+ label_set_ids_->swap(new_ids);
+}
+
+void S2PolygonLayer::Build(const Graph& g, S2Error* error) {
+ if (label_set_ids_) label_set_ids_->clear();
+
+ // It's tricky to compute the edge labels for S2Polygons because the
+ // S2Polygon::Init methods can reorder and/or invert the loops. We handle
+ // this by remembering the original vector index of each loop and whether or
+ // not the loop contained S2::Origin(). By comparing this with the final
+ // S2Polygon loops we can fix up the edge labels appropriately.
+ LoopMap loop_map;
+ if (g.num_edges() == 0) {
+ // The polygon is either full or empty.
+ if (g.IsFullPolygon(error)) {
+ polygon_->Init(make_unique<S2Loop>(S2Loop::kFull()));
+ } else {
+ polygon_->InitNested(vector<unique_ptr<S2Loop>>{});
+ }
+ } else if (g.options().edge_type() == EdgeType::DIRECTED) {
+ vector<Graph::EdgeLoop> edge_loops;
+ if (!g.GetDirectedLoops(LoopType::SIMPLE, &edge_loops, error)) {
+ return;
+ }
+ vector<unique_ptr<S2Loop>> loops;
+ AppendS2Loops(g, edge_loops, &loops);
+ AppendEdgeLabels(g, edge_loops);
+ vector<Graph::EdgeLoop>().swap(edge_loops); // Release memory
+ InitLoopMap(loops, &loop_map);
+ polygon_->InitOriented(std::move(loops));
+ } else {
+ vector<Graph::UndirectedComponent> components;
+ if (!g.GetUndirectedComponents(LoopType::SIMPLE, &components, error)) {
+ return;
+ }
+ // It doesn't really matter which complement of each component we use,
+ // since below we normalize all the loops so that they enclose at most
+ // half of the sphere (to ensure that the loops can always be nested).
+ //
+ // The only reason to prefer one over the other is that when there are
+ // multiple loops that touch, only one of the two complements matches the
+ // structure of the input loops. GetUndirectedComponents() tries to
+ // ensure that this is always complement 0 of each component.
+ vector<unique_ptr<S2Loop>> loops;
+ for (const auto& component : components) {
+ AppendS2Loops(g, component[0], &loops);
+ AppendEdgeLabels(g, component[0]);
+ }
+ vector<Graph::UndirectedComponent>().swap(components); // Release memory
+ InitLoopMap(loops, &loop_map);
+ for (const auto& loop : loops) loop->Normalize();
+ polygon_->InitNested(std::move(loops));
+ }
+ ReorderEdgeLabels(loop_map);
+ if (options_.validate()) {
+ polygon_->FindValidationError(error);
+ }
+}
+
+} // namespace s2builderutil
--- /dev/null
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2builderutil_s2polyline_layer.h"
+
+#include "s2/s2debug.h"
+
+using std::vector;
+
+using EdgeType = S2Builder::EdgeType;
+using Graph = S2Builder::Graph;
+using GraphOptions = S2Builder::GraphOptions;
+using Label = S2Builder::Label;
+
+using DegenerateEdges = GraphOptions::DegenerateEdges;
+using DuplicateEdges = GraphOptions::DuplicateEdges;
+using SiblingPairs = GraphOptions::SiblingPairs;
+
+using EdgeId = Graph::EdgeId;
+using PolylineType = Graph::PolylineType;
+
+namespace s2builderutil {
+
+S2PolylineLayer::S2PolylineLayer(S2Polyline* polyline,
+ const S2PolylineLayer::Options& options) {
+ Init(polyline, nullptr, nullptr, options);
+}
+
+S2PolylineLayer::S2PolylineLayer(
+ S2Polyline* polyline, LabelSetIds* label_set_ids,
+ IdSetLexicon* label_set_lexicon, const Options& options) {
+ Init(polyline, label_set_ids, label_set_lexicon, options);
+}
+
+void S2PolylineLayer::Init(S2Polyline* polyline, LabelSetIds* label_set_ids,
+ IdSetLexicon* label_set_lexicon,
+ const Options& options) {
+ S2_DCHECK_EQ(label_set_ids == nullptr, label_set_lexicon == nullptr);
+ polyline_ = polyline;
+ label_set_ids_ = label_set_ids;
+ label_set_lexicon_ = label_set_lexicon;
+ options_ = options;
+
+ if (options_.validate()) {
+ polyline_->set_s2debug_override(S2Debug::DISABLE);
+ }
+}
+
+GraphOptions S2PolylineLayer::graph_options() const {
+ // Remove edges that collapse to a single vertex, but keep duplicate and
+ // sibling edges, since merging duplicates or discarding siblings can make
+ // it impossible to assemble the edges into a single polyline.
+ return GraphOptions(options_.edge_type(), DegenerateEdges::DISCARD,
+ DuplicateEdges::KEEP, SiblingPairs::KEEP);
+}
+
+void S2PolylineLayer::Build(const Graph& g, S2Error* error) {
+ if (g.num_edges() == 0) {
+ polyline_->Init(vector<S2Point>{});
+ return;
+ }
+ vector<Graph::EdgePolyline> edge_polylines =
+ g.GetPolylines(PolylineType::WALK);
+ if (edge_polylines.size() != 1) {
+ error->Init(S2Error::BUILDER_EDGES_DO_NOT_FORM_POLYLINE,
+ "Input edges cannot be assembled into polyline");
+ return;
+ }
+ const Graph::EdgePolyline& edge_polyline = edge_polylines[0];
+ vector<S2Point> vertices; // Temporary storage for vertices.
+ vertices.reserve(edge_polyline.size());
+ vertices.push_back(g.vertex(g.edge(edge_polyline[0]).first));
+ for (EdgeId e : edge_polyline) {
+ vertices.push_back(g.vertex(g.edge(e).second));
+ }
+ if (label_set_ids_) {
+ Graph::LabelFetcher fetcher(g, options_.edge_type());
+ vector<Label> labels; // Temporary storage for labels.
+ label_set_ids_->reserve(edge_polyline.size());
+ for (EdgeId e : edge_polyline) {
+ fetcher.Fetch(e, &labels);
+ label_set_ids_->push_back(label_set_lexicon_->Add(labels));
+ }
+ }
+ polyline_->Init(vertices);
+ if (options_.validate()) {
+ polyline_->FindValidationError(error);
+ }
+}
+
+} // namespace s2builderutil
--- /dev/null
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2builderutil_s2polyline_vector_layer.h"
+
+#include <memory>
+
+using std::unique_ptr;
+using std::vector;
+
+using EdgeType = S2Builder::EdgeType;
+using Graph = S2Builder::Graph;
+using GraphOptions = S2Builder::GraphOptions;
+using Label = S2Builder::Label;
+
+using DegenerateEdges = GraphOptions::DegenerateEdges;
+using DuplicateEdges = GraphOptions::DuplicateEdges;
+using SiblingPairs = GraphOptions::SiblingPairs;
+
+using EdgeId = Graph::EdgeId;
+
+namespace s2builderutil {
+
+S2PolylineVectorLayer::S2PolylineVectorLayer(
+ vector<unique_ptr<S2Polyline>>* polylines,
+ const S2PolylineVectorLayer::Options& options) {
+ Init(polylines, nullptr, nullptr, options);
+}
+
+S2PolylineVectorLayer::S2PolylineVectorLayer(
+ vector<unique_ptr<S2Polyline>>* polylines, LabelSetIds* label_set_ids,
+ IdSetLexicon* label_set_lexicon, const Options& options) {
+ Init(polylines, label_set_ids, label_set_lexicon, options);
+}
+
+void S2PolylineVectorLayer::Init(vector<unique_ptr<S2Polyline>>* polylines,
+ LabelSetIds* label_set_ids,
+ IdSetLexicon* label_set_lexicon,
+ const Options& options) {
+ S2_DCHECK_EQ(label_set_ids == nullptr, label_set_lexicon == nullptr);
+ polylines_ = polylines;
+ label_set_ids_ = label_set_ids;
+ label_set_lexicon_ = label_set_lexicon;
+ options_ = options;
+}
+
+GraphOptions S2PolylineVectorLayer::graph_options() const {
+ return GraphOptions(options_.edge_type(), DegenerateEdges::DISCARD,
+ options_.duplicate_edges(), options_.sibling_pairs());
+}
+
+void S2PolylineVectorLayer::Build(const Graph& g, S2Error* error) {
+ vector<Graph::EdgePolyline> edge_polylines = g.GetPolylines(
+ options_.polyline_type());
+ polylines_->reserve(edge_polylines.size());
+ if (label_set_ids_) label_set_ids_->reserve(edge_polylines.size());
+ vector<S2Point> vertices; // Temporary storage for vertices.
+ vector<Label> labels; // Temporary storage for labels.
+ for (const auto& edge_polyline : edge_polylines) {
+ vertices.push_back(g.vertex(g.edge(edge_polyline[0]).first));
+ for (EdgeId e : edge_polyline) {
+ vertices.push_back(g.vertex(g.edge(e).second));
+ }
+ S2Polyline* polyline = new S2Polyline(vertices,
+ options_.s2debug_override());
+ vertices.clear();
+ if (options_.validate()) {
+ polyline->FindValidationError(error);
+ }
+ polylines_->emplace_back(polyline);
+ if (label_set_ids_) {
+ Graph::LabelFetcher fetcher(g, options_.edge_type());
+ vector<LabelSetId> polyline_labels;
+ polyline_labels.reserve(edge_polyline.size());
+ for (EdgeId e : edge_polyline) {
+ fetcher.Fetch(e, &labels);
+ polyline_labels.push_back(label_set_lexicon_->Add(labels));
+ }
+ label_set_ids_->push_back(std::move(polyline_labels));
+ }
+ }
+}
+
+} // namespace s2builderutil
--- /dev/null
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2builderutil_snap_functions.h"
+
+#include <algorithm>
+#include <cfloat>
+#include <cmath>
+#include <memory>
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2latlng.h"
+#include "s2/s2metrics.h"
+
+using absl::make_unique;
+using std::max;
+using std::min;
+using std::unique_ptr;
+
+namespace s2builderutil {
+
+const int IntLatLngSnapFunction::kMinExponent;
+const int IntLatLngSnapFunction::kMaxExponent;
+
+IdentitySnapFunction::IdentitySnapFunction()
+ : snap_radius_(S1Angle::Zero()) {
+}
+
+IdentitySnapFunction::IdentitySnapFunction(S1Angle snap_radius) {
+ set_snap_radius(snap_radius);
+}
+
+void IdentitySnapFunction::set_snap_radius(S1Angle snap_radius) {
+ S2_DCHECK_LE(snap_radius, kMaxSnapRadius());
+ snap_radius_ = snap_radius;
+}
+
+S1Angle IdentitySnapFunction::snap_radius() const {
+ return snap_radius_;
+}
+
+S1Angle IdentitySnapFunction::min_vertex_separation() const {
+ // Since SnapFunction does not move the input point, output vertices are
+ // separated by the full snap_radius().
+ return snap_radius_;
+}
+
+S1Angle IdentitySnapFunction::min_edge_vertex_separation() const {
+ // In the worst case configuration, the edge separation is half of the
+ // vertex separation.
+ return 0.5 * snap_radius_;
+}
+
+S2Point IdentitySnapFunction::SnapPoint(const S2Point& point) const {
+ return point;
+}
+
+unique_ptr<S2Builder::SnapFunction> IdentitySnapFunction::Clone() const {
+ return make_unique<IdentitySnapFunction>(*this);
+}
+
+
+S2CellIdSnapFunction::S2CellIdSnapFunction() {
+ set_level(S2CellId::kMaxLevel);
+}
+
+S2CellIdSnapFunction::S2CellIdSnapFunction(int level) {
+ set_level(level);
+}
+
+void S2CellIdSnapFunction::set_level(int level) {
+ S2_DCHECK_GE(level, 0);
+ S2_DCHECK_LE(level, S2CellId::kMaxLevel);
+ level_ = level;
+ set_snap_radius(MinSnapRadiusForLevel(level));
+}
+
+int S2CellIdSnapFunction::level() const {
+ return level_;
+}
+
+void S2CellIdSnapFunction::set_snap_radius(S1Angle snap_radius) {
+ S2_DCHECK_GE(snap_radius, MinSnapRadiusForLevel(level()));
+ S2_DCHECK_LE(snap_radius, kMaxSnapRadius());
+ snap_radius_ = snap_radius;
+}
+
+S1Angle S2CellIdSnapFunction::snap_radius() const {
+ return snap_radius_;
+}
+
+S1Angle S2CellIdSnapFunction::MinSnapRadiusForLevel(int level) {
+ // snap_radius() needs to be an upper bound on the true distance that a
+ // point can move when snapped, taking into account numerical errors.
+ //
+ // The maximum error when converting from an S2Point to an S2CellId is
+ // S2::kMaxDiag.deriv() * DBL_EPSILON. The maximum error when converting an
+ // S2CellId center back to an S2Point is 1.5 * DBL_EPSILON. These add up to
+ // just slightly less than 4 * DBL_EPSILON.
+ return S1Angle::Radians(0.5 * S2::kMaxDiag.GetValue(level) + 4 * DBL_EPSILON);
+}
+
+int S2CellIdSnapFunction::LevelForMaxSnapRadius(S1Angle snap_radius) {
+ // When choosing a level, we need to acount for the error bound of
+ // 4 * DBL_EPSILON that is added by MinSnapRadiusForLevel().
+ return S2::kMaxDiag.GetLevelForMaxValue(
+ 2 * (snap_radius.radians() - 4 * DBL_EPSILON));
+}
+
+S1Angle S2CellIdSnapFunction::min_vertex_separation() const {
+ // We have three different bounds for the minimum vertex separation: one is
+ // a constant bound, one is proportional to snap_radius, and one is equal to
+ // snap_radius minus a constant. These bounds give the best results for
+ // small, medium, and large snap radii respectively. We return the maximum
+ // of the three bounds.
+ //
+ // 1. Constant bound: Vertices are always separated by at least
+ // kMinEdge(level), the minimum edge length for the chosen snap level.
+ //
+ // 2. Proportional bound: It can be shown that in the plane, the worst-case
+ // configuration has a vertex separation of 2 / sqrt(13) * snap_radius.
+ // This is verified in the unit test, except that on the sphere the ratio
+ // is slightly smaller at cell level 2 (0.54849 vs. 0.55470). We reduce
+ // that value a bit more below to be conservative.
+ //
+ // 3. Best asymptotic bound: This bound bound is derived by observing we
+ // only select a new site when it is at least snap_radius() away from all
+ // existing sites, and the site can move by at most 0.5 * kMaxDiag(level)
+ // when snapped.
+ S1Angle min_edge = S1Angle::Radians(S2::kMinEdge.GetValue(level_));
+ S1Angle max_diag = S1Angle::Radians(S2::kMaxDiag.GetValue(level_));
+ return max(min_edge,
+ max(0.548 * snap_radius_, // 2 / sqrt(13) in the plane
+ snap_radius_ - 0.5 * max_diag));
+}
+
+S1Angle S2CellIdSnapFunction::min_edge_vertex_separation() const {
+ // Similar to min_vertex_separation(), in this case we have four bounds: a
+ // constant bound that holds only at the minimum snap radius, a constant
+ // bound that holds for any snap radius, a bound that is proportional to
+ // snap_radius, and a bound that is equal to snap_radius minus a constant.
+ //
+ // 1. Constant bounds:
+ //
+ // (a) At the minimum snap radius for a given level, it can be shown that
+ // vertices are separated from edges by at least 0.5 * kMinDiag(level) in
+ // the plane. The unit test verifies this, except that on the sphere the
+ // worst case is slightly better: 0.5652980068 * kMinDiag(level).
+ //
+ // (b) Otherwise, for arbitrary snap radii the worst-case configuration
+ // in the plane has an edge-vertex separation of sqrt(3/19) *
+ // kMinDiag(level), where sqrt(3/19) is about 0.3973597071. The unit
+ // test verifies that the bound is slighty better on the sphere:
+ // 0.3973595687 * kMinDiag(level).
+ //
+ // 2. Proportional bound: In the plane, the worst-case configuration has an
+ // edge-vertex separation of 2 * sqrt(3/247) * snap_radius, which is
+ // about 0.2204155075. The unit test verifies this, except that on the
+ // sphere the bound is slightly worse for certain large S2Cells: the
+ // minimum ratio occurs at cell level 6, and is about 0.2196666953.
+ //
+ // 3. Best asymptotic bound: If snap_radius() is large compared to the
+ // minimum snap radius, then the best bound is achieved by 3 sites on a
+ // circular arc of radius "snap_radius", spaced "min_vertex_separation"
+ // apart. An input edge passing just to one side of the center of the
+ // circle intersects the Voronoi regions of the two end sites but not the
+ // Voronoi region of the center site, and gives an edge separation of
+ // (min_vertex_separation ** 2) / (2 * snap_radius). This bound
+ // approaches 0.5 * snap_radius for large snap radii, i.e. the minimum
+ // edge-vertex separation approaches half of the minimum vertex
+ // separation as the snap radius becomes large compared to the cell size.
+
+ S1Angle min_diag = S1Angle::Radians(S2::kMinDiag.GetValue(level_));
+ if (snap_radius() == MinSnapRadiusForLevel(level_)) {
+ // This bound only holds when the minimum snap radius is being used.
+ return 0.565 * min_diag; // 0.500 in the plane
+ }
+ // Otherwise, these bounds hold for any snap_radius().
+ S1Angle vertex_sep = min_vertex_separation();
+ return max(0.397 * min_diag, // sqrt(3 / 19) in the plane
+ max(0.219 * snap_radius_, // 2 * sqrt(3 / 247) in the plane
+ 0.5 * (vertex_sep / snap_radius_) * vertex_sep));
+}
+
+S2Point S2CellIdSnapFunction::SnapPoint(const S2Point& point) const {
+ return S2CellId(point).parent(level_).ToPoint();
+}
+
+unique_ptr<S2Builder::SnapFunction> S2CellIdSnapFunction::Clone() const {
+ return make_unique<S2CellIdSnapFunction>(*this);
+}
+
+IntLatLngSnapFunction::IntLatLngSnapFunction()
+ : exponent_(-1), snap_radius_(), from_degrees_(0), to_degrees_(0) {
+}
+
+IntLatLngSnapFunction::IntLatLngSnapFunction(int exponent) {
+ set_exponent(exponent);
+}
+
+void IntLatLngSnapFunction::set_exponent(int exponent) {
+ S2_DCHECK_GE(exponent, kMinExponent);
+ S2_DCHECK_LE(exponent, kMaxExponent);
+ exponent_ = exponent;
+ set_snap_radius(MinSnapRadiusForExponent(exponent));
+
+ // Precompute the scale factors needed for snapping. Note that these
+ // calculations need to exactly match the ones in s1angle.h to ensure
+ // that the same S2Points are generated.
+ double power = 1;
+ for (int i = 0; i < exponent; ++i) power *= 10;
+ from_degrees_ = power;
+ to_degrees_ = 1 / power;
+}
+
+int IntLatLngSnapFunction::exponent() const {
+ return exponent_;
+}
+
+void IntLatLngSnapFunction::set_snap_radius(S1Angle snap_radius) {
+ S2_DCHECK_GE(snap_radius, MinSnapRadiusForExponent(exponent()));
+ S2_DCHECK_LE(snap_radius, kMaxSnapRadius());
+ snap_radius_ = snap_radius;
+}
+
+S1Angle IntLatLngSnapFunction::snap_radius() const {
+ return snap_radius_;
+}
+
+S1Angle IntLatLngSnapFunction::MinSnapRadiusForExponent(int exponent) {
+ // snap_radius() needs to be an upper bound on the true distance that a
+ // point can move when snapped, taking into account numerical errors.
+ //
+ // The maximum errors in latitude and longitude can be bounded as
+ // follows (as absolute errors in terms of DBL_EPSILON):
+ //
+ // Latitude Longitude
+ // Convert to S2LatLng: 1.000 1.000
+ // Convert to degrees: 1.032 2.063
+ // Scale by 10**exp: 0.786 1.571
+ // Round to integer: 0.5 * S1Angle::Degrees(to_degrees_)
+ // Scale by 10**(-exp): 1.375 2.749
+ // Convert to radians: 1.252 1.503
+ // ------------------------------------------------------------
+ // Total (except for rounding) 5.445 8.886
+ //
+ // The maximum error when converting the S2LatLng back to an S2Point is
+ //
+ // sqrt(2) * (maximum error in latitude or longitude) + 1.5 * DBL_EPSILON
+ //
+ // which works out to (9 * sqrt(2) + 1.5) * DBL_EPSILON radians. Finally
+ // we need to consider the effect of rounding to integer coordinates
+ // (much larger than the errors above), which can change the position by
+ // up to (sqrt(2) * 0.5 * to_degrees_) radians.
+ double power = 1;
+ for (int i = 0; i < exponent; ++i) power *= 10;
+ return (S1Angle::Degrees(M_SQRT1_2 / power) +
+ S1Angle::Radians((9 * M_SQRT2 + 1.5) * DBL_EPSILON));
+}
+
+int IntLatLngSnapFunction::ExponentForMaxSnapRadius(S1Angle snap_radius) {
+ // When choosing an exponent, we need to acount for the error bound of
+ // (9 * sqrt(2) + 1.5) * DBL_EPSILON added by MinSnapRadiusForExponent().
+ snap_radius -= S1Angle::Radians((9 * M_SQRT2 + 1.5) * DBL_EPSILON);
+ snap_radius = max(snap_radius, S1Angle::Radians(1e-30));
+ double exponent = log10(M_SQRT1_2 / snap_radius.degrees());
+
+ // There can be small errors in the calculation above, so to ensure that
+ // this function is the inverse of MinSnapRadiusForExponent() we subtract a
+ // small error tolerance.
+ return max(kMinExponent,
+ min(kMaxExponent,
+ static_cast<int>(std::ceil(exponent - 2 * DBL_EPSILON))));
+}
+
+S1Angle IntLatLngSnapFunction::min_vertex_separation() const {
+ // We have two bounds for the minimum vertex separation: one is proportional
+ // to snap_radius, and one is equal to snap_radius minus a constant. These
+ // bounds give the best results for small and large snap radii respectively.
+ // We return the maximum of the two bounds.
+ //
+ // 1. Proportional bound: It can be shown that in the plane, the worst-case
+ // configuration has a vertex separation of (sqrt(2) / 3) * snap_radius.
+ // This is verified in the unit test, except that on the sphere the ratio
+ // is slightly smaller (0.471337 vs. 0.471404). We reduce that value a
+ // bit more below to be conservative.
+ //
+ // 2. Best asymptotic bound: This bound bound is derived by observing we
+ // only select a new site when it is at least snap_radius() away from all
+ // existing sites, and snapping a vertex can move it by up to
+ // ((1 / sqrt(2)) * to_degrees_) degrees.
+ return max(0.471 * snap_radius_, // sqrt(2) / 3 in the plane
+ snap_radius_ - S1Angle::Degrees(M_SQRT1_2 * to_degrees_));
+}
+
+S1Angle IntLatLngSnapFunction::min_edge_vertex_separation() const {
+ // Similar to min_vertex_separation(), in this case we have three bounds:
+ // one is a constant bound, one is proportional to snap_radius, and one is
+ // equal to snap_radius minus a constant.
+ //
+ // 1. Constant bound: In the plane, the worst-case configuration has an
+ // edge-vertex separation of ((1 / sqrt(13)) * to_degrees_) degrees.
+ // The unit test verifies this, except that on the sphere the ratio is
+ // slightly lower when small exponents such as E1 are used
+ // (0.2772589 vs 0.2773501).
+ //
+ // 2. Proportional bound: In the plane, the worst-case configuration has an
+ // edge-vertex separation of (2 / 9) * snap_radius (0.222222222222). The
+ // unit test verifies this, except that on the sphere the bound can be
+ // slightly worse with large exponents (e.g., E9) due to small numerical
+ // errors (0.222222126756717).
+ //
+ // 3. Best asymptotic bound: If snap_radius() is large compared to the
+ // minimum snap radius, then the best bound is achieved by 3 sites on a
+ // circular arc of radius "snap_radius", spaced "min_vertex_separation"
+ // apart (see S2CellIdSnapFunction::min_edge_vertex_separation). This
+ // bound approaches 0.5 * snap_radius as the snap radius becomes large
+ // relative to the grid spacing.
+
+ S1Angle vertex_sep = min_vertex_separation();
+ return max(0.277 * S1Angle::Degrees(to_degrees_), // 1/sqrt(13) in the plane
+ max(0.222 * snap_radius_, // 2/9 in the plane
+ 0.5 * (vertex_sep / snap_radius_) * vertex_sep));
+}
+
+S2Point IntLatLngSnapFunction::SnapPoint(const S2Point& point) const {
+ S2_DCHECK_GE(exponent_, 0); // Make sure the snap function was initialized.
+ S2LatLng input(point);
+ int64 lat = MathUtil::FastInt64Round(input.lat().degrees() * from_degrees_);
+ int64 lng = MathUtil::FastInt64Round(input.lng().degrees() * from_degrees_);
+ return S2LatLng::FromDegrees(lat * to_degrees_, lng * to_degrees_).ToPoint();
+}
+
+unique_ptr<S2Builder::SnapFunction> IntLatLngSnapFunction::Clone() const {
+ return make_unique<IntLatLngSnapFunction>(*this);
+}
+
+} // namespace s2builderutil
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2builderutil_testing.h"
+
+namespace s2builderutil {
+
+void GraphClone::Init(const S2Builder::Graph& g) {
+ options_ = g.options();
+ vertices_ = g.vertices();
+ edges_ = g.edges();
+ input_edge_id_set_ids_ = g.input_edge_id_set_ids();
+ input_edge_id_set_lexicon_ = g.input_edge_id_set_lexicon();
+ label_set_ids_ = g.label_set_ids();
+ label_set_lexicon_ = g.label_set_lexicon();
+ is_full_polygon_predicate_ = g.is_full_polygon_predicate();
+ g_ = S2Builder::Graph(
+ options_, &vertices_, &edges_, &input_edge_id_set_ids_,
+ &input_edge_id_set_lexicon_, &label_set_ids_, &label_set_lexicon_,
+ is_full_polygon_predicate_);
+}
+
+} // namespace s2builderutil
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2cap.h"
+
+#include <cfloat>
+#include <cmath>
+#include <iosfwd>
+#include <vector>
+
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+#include "s2/r1interval.h"
+#include "s2/s1interval.h"
+#include "s2/s2cell.h"
+#include "s2/s2debug.h"
+#include "s2/s2edge_distances.h"
+#include "s2/s2latlng.h"
+#include "s2/s2latlng_rect.h"
+#include "s2/s2metrics.h"
+#include "s2/s2pointutil.h"
+#include "s2/util/math/vector.h"
+
+using std::fabs;
+using std::max;
+using std::vector;
+
+double S2Cap::GetArea() const {
+ return 2 * M_PI * max(0.0, height());
+}
+
+S2Point S2Cap::GetCentroid() const {
+ // From symmetry, the centroid of the cap must be somewhere on the line
+ // from the origin to the center of the cap on the surface of the sphere.
+ // When a sphere is divided into slices of constant thickness by a set of
+ // parallel planes, all slices have the same surface area. This implies
+ // that the radial component of the centroid is simply the midpoint of the
+ // range of radial distances spanned by the cap. That is easily computed
+ // from the cap height.
+ if (is_empty()) return S2Point();
+ double r = 1.0 - 0.5 * height();
+ return r * GetArea() * center_;
+}
+
+S2Cap S2Cap::Complement() const {
+ // The complement of a full cap is an empty cap, not a singleton.
+ // Also make sure that the complement of an empty cap is full.
+ if (is_full()) return Empty();
+ if (is_empty()) return Full();
+ return S2Cap(-center_, S1ChordAngle::FromLength2(4 - radius_.length2()));
+}
+
+bool S2Cap::Contains(const S2Cap& other) const {
+ if (is_full() || other.is_empty()) return true;
+ return radius_ >= S1ChordAngle(center_, other.center_) + other.radius_;
+}
+
+bool S2Cap::Intersects(const S2Cap& other) const {
+ if (is_empty() || other.is_empty()) return false;
+ return radius_ + other.radius_ >= S1ChordAngle(center_, other.center_);
+}
+
+bool S2Cap::InteriorIntersects(const S2Cap& other) const {
+ // Make sure this cap has an interior and the other cap is non-empty.
+ if (radius_.length2() <= 0 || other.is_empty()) return false;
+ return radius_ + other.radius_ > S1ChordAngle(center_, other.center_);
+}
+
+void S2Cap::AddPoint(const S2Point& p) {
+ // Compute the squared chord length, then convert it into a height.
+ S2_DCHECK(S2::IsUnitLength(p));
+ if (is_empty()) {
+ center_ = p;
+ radius_ = S1ChordAngle::Zero();
+ } else {
+ // After calling cap.AddPoint(p), cap.Contains(p) must be true. However
+ // we don't need to do anything special to achieve this because Contains()
+ // does exactly the same distance calculation that we do here.
+ radius_ = max(radius_, S1ChordAngle(center_, p));
+ }
+}
+
+void S2Cap::AddCap(const S2Cap& other) {
+ if (is_empty()) {
+ *this = other;
+ } else if (!other.is_empty()) {
+ // We round up the distance to ensure that the cap is actually contained.
+ // TODO(ericv): Do some error analysis in order to guarantee this.
+ S1ChordAngle dist = S1ChordAngle(center_, other.center_) + other.radius_;
+ radius_ = max(radius_, dist.PlusError(DBL_EPSILON * dist.length2()));
+ }
+}
+
+S2Cap S2Cap::Expanded(S1Angle distance) const {
+ S2_DCHECK_GE(distance.radians(), 0);
+ if (is_empty()) return Empty();
+ return S2Cap(center_, radius_ + S1ChordAngle(distance));
+}
+
+S2Cap S2Cap::Union(const S2Cap& other) const {
+ if (radius_ < other.radius_) {
+ return other.Union(*this);
+ }
+ if (is_full() || other.is_empty()) {
+ return *this;
+ }
+ // This calculation would be more efficient using S1ChordAngles.
+ S1Angle this_radius = GetRadius();
+ S1Angle other_radius = other.GetRadius();
+ S1Angle distance(center(), other.center());
+ if (this_radius >= distance + other_radius) {
+ return *this;
+ } else {
+ S1Angle result_radius = 0.5 * (distance + this_radius + other_radius);
+ S2Point result_center = S2::InterpolateAtDistance(
+ 0.5 * (distance - this_radius + other_radius),
+ center(),
+ other.center());
+ return S2Cap(result_center, result_radius);
+ }
+}
+
+S2Cap* S2Cap::Clone() const {
+ return new S2Cap(*this);
+}
+
+S2Cap S2Cap::GetCapBound() const {
+ return *this;
+}
+
+S2LatLngRect S2Cap::GetRectBound() const {
+ if (is_empty()) return S2LatLngRect::Empty();
+
+ // Convert the center to a (lat,lng) pair, and compute the cap angle.
+ S2LatLng center_ll(center_);
+ double cap_angle = GetRadius().radians();
+
+ bool all_longitudes = false;
+ double lat[2], lng[2];
+ lng[0] = -M_PI;
+ lng[1] = M_PI;
+
+ // Check whether cap includes the south pole.
+ lat[0] = center_ll.lat().radians() - cap_angle;
+ if (lat[0] <= -M_PI_2) {
+ lat[0] = -M_PI_2;
+ all_longitudes = true;
+ }
+ // Check whether cap includes the north pole.
+ lat[1] = center_ll.lat().radians() + cap_angle;
+ if (lat[1] >= M_PI_2) {
+ lat[1] = M_PI_2;
+ all_longitudes = true;
+ }
+ if (!all_longitudes) {
+ // Compute the range of longitudes covered by the cap. We use the law
+ // of sines for spherical triangles. Consider the triangle ABC where
+ // A is the north pole, B is the center of the cap, and C is the point
+ // of tangency between the cap boundary and a line of longitude. Then
+ // C is a right angle, and letting a,b,c denote the sides opposite A,B,C,
+ // we have sin(a)/sin(A) = sin(c)/sin(C), or sin(A) = sin(a)/sin(c).
+ // Here "a" is the cap angle, and "c" is the colatitude (90 degrees
+ // minus the latitude). This formula also works for negative latitudes.
+ //
+ // The formula for sin(a) follows from the relationship h = 1 - cos(a).
+
+ double sin_a = sin(radius_);
+ double sin_c = cos(center_ll.lat().radians());
+ if (sin_a <= sin_c) {
+ double angle_A = asin(sin_a / sin_c);
+ lng[0] = remainder(center_ll.lng().radians() - angle_A, 2 * M_PI);
+ lng[1] = remainder(center_ll.lng().radians() + angle_A, 2 * M_PI);
+ }
+ }
+ return S2LatLngRect(R1Interval(lat[0], lat[1]),
+ S1Interval(lng[0], lng[1]));
+}
+
+// Computes a covering of the S2Cap. In general the covering consists of at
+// most 4 cells except for very large caps, which may need up to 6 cells.
+// The output is not sorted.
+void S2Cap::GetCellUnionBound(vector<S2CellId>* cell_ids) const {
+ // TODO(ericv): The covering could be made quite a bit tighter by mapping
+ // the cap to a rectangle in (i,j)-space and finding a covering for that.
+ cell_ids->clear();
+
+ // Find the maximum level such that the cap contains at most one cell vertex
+ // and such that S2CellId::AppendVertexNeighbors() can be called.
+ int level = S2::kMinWidth.GetLevelForMinValue(GetRadius().radians()) - 1;
+
+ // If level < 0, then more than three face cells are required.
+ if (level < 0) {
+ cell_ids->reserve(6);
+ for (int face = 0; face < 6; ++face) {
+ cell_ids->push_back(S2CellId::FromFace(face));
+ }
+ } else {
+ // The covering consists of the 4 cells at the given level that share the
+ // cell vertex that is closest to the cap center.
+ cell_ids->reserve(4);
+ S2CellId(center_).AppendVertexNeighbors(level, cell_ids);
+ }
+}
+
+bool S2Cap::Intersects(const S2Cell& cell, const S2Point* vertices) const {
+ // Return true if this cap intersects any point of 'cell' excluding its
+ // vertices (which are assumed to already have been checked).
+
+ // If the cap is a hemisphere or larger, the cell and the complement of the
+ // cap are both convex. Therefore since no vertex of the cell is contained,
+ // no other interior point of the cell is contained either.
+ if (radius_ >= S1ChordAngle::Right()) return false;
+
+ // We need to check for empty caps due to the center check just below.
+ if (is_empty()) return false;
+
+ // Optimization: return true if the cell contains the cap center. (This
+ // allows half of the edge checks below to be skipped.)
+ if (cell.Contains(center_)) return true;
+
+ // At this point we know that the cell does not contain the cap center,
+ // and the cap does not contain any cell vertex. The only way that they
+ // can intersect is if the cap intersects the interior of some edge.
+
+ double sin2_angle = sin2(radius_);
+ for (int k = 0; k < 4; ++k) {
+ S2Point edge = cell.GetEdgeRaw(k);
+ double dot = center_.DotProd(edge);
+ if (dot > 0) {
+ // The center is in the interior half-space defined by the edge. We don't
+ // need to consider these edges, since if the cap intersects this edge
+ // then it also intersects the edge on the opposite side of the cell
+ // (because we know the center is not contained with the cell).
+ continue;
+ }
+ // The Norm2() factor is necessary because "edge" is not normalized.
+ if (dot * dot > sin2_angle * edge.Norm2()) {
+ return false; // Entire cap is on the exterior side of this edge.
+ }
+ // Otherwise, the great circle containing this edge intersects
+ // the interior of the cap. We just need to check whether the point
+ // of closest approach occurs between the two edge endpoints.
+ Vector3_d dir = edge.CrossProd(center_);
+ if (dir.DotProd(vertices[k]) < 0 && dir.DotProd(vertices[(k+1)&3]) > 0)
+ return true;
+ }
+ return false;
+}
+
+bool S2Cap::Contains(const S2Cell& cell) const {
+ // If the cap does not contain all cell vertices, return false.
+ // We check the vertices before taking the Complement() because we can't
+ // accurately represent the complement of a very small cap (a height
+ // of 2-epsilon is rounded off to 2).
+ S2Point vertices[4];
+ for (int k = 0; k < 4; ++k) {
+ vertices[k] = cell.GetVertex(k);
+ if (!Contains(vertices[k])) return false;
+ }
+ // Otherwise, return true if the complement of the cap does not intersect
+ // the cell. (This test is slightly conservative, because technically we
+ // want Complement().InteriorIntersects() here.)
+ return !Complement().Intersects(cell, vertices);
+}
+
+bool S2Cap::MayIntersect(const S2Cell& cell) const {
+ // If the cap contains any cell vertex, return true.
+ S2Point vertices[4];
+ for (int k = 0; k < 4; ++k) {
+ vertices[k] = cell.GetVertex(k);
+ if (Contains(vertices[k])) return true;
+ }
+ return Intersects(cell, vertices);
+}
+
+bool S2Cap::Contains(const S2Point& p) const {
+ S2_DCHECK(S2::IsUnitLength(p));
+ return S1ChordAngle(center_, p) <= radius_;
+}
+
+bool S2Cap::InteriorContains(const S2Point& p) const {
+ S2_DCHECK(S2::IsUnitLength(p));
+ return is_full() || S1ChordAngle(center_, p) < radius_;
+}
+
+bool S2Cap::operator==(const S2Cap& other) const {
+ return (center_ == other.center_ && radius_ == other.radius_) ||
+ (is_empty() && other.is_empty()) ||
+ (is_full() && other.is_full());
+}
+
+bool S2Cap::ApproxEquals(const S2Cap& other, S1Angle max_error_angle) const {
+ const double max_error = max_error_angle.radians();
+ const double r2 = radius_.length2();
+ const double other_r2 = other.radius_.length2();
+ return (S2::ApproxEquals(center_, other.center_, max_error_angle) &&
+ fabs(r2 - other_r2) <= max_error) ||
+ (is_empty() && other_r2 <= max_error) ||
+ (other.is_empty() && r2 <= max_error) ||
+ (is_full() && other_r2 >= 2 - max_error) ||
+ (other.is_full() && r2 >= 2 - max_error);
+}
+
+std::ostream& operator<<(std::ostream& os, const S2Cap& cap) {
+ return os << "[Center=" << cap.center()
+ << ", Radius=" << cap.GetRadius() << "]";
+}
+
+void S2Cap::Encode(Encoder* encoder) const {
+ encoder->Ensure(4 * sizeof(double));
+
+ encoder->putdouble(center_.x());
+ encoder->putdouble(center_.y());
+ encoder->putdouble(center_.z());
+ encoder->putdouble(radius_.length2());
+
+ S2_DCHECK_GE(encoder->avail(), 0);
+}
+
+bool S2Cap::Decode(Decoder* decoder) {
+ if (decoder->avail() < 4 * sizeof(double)) return false;
+
+ double x = decoder->getdouble();
+ double y = decoder->getdouble();
+ double z = decoder->getdouble();
+ center_ = S2Point(x, y, z);
+ radius_ = S1ChordAngle::FromLength2(decoder->getdouble());
+
+ if (FLAGS_s2debug) {
+ S2_CHECK(is_valid()) << "Invalid S2Cap: " << *this;
+ }
+ return true;
+}
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2cell.h"
+
+#include <algorithm>
+#include <cfloat>
+#include <cmath>
+#include <iomanip>
+
+#include "s2/base/logging.h"
+#include "s2/r1interval.h"
+#include "s2/r2.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s1interval.h"
+#include "s2/s2cap.h"
+#include "s2/s2coords.h"
+#include "s2/s2edge_crosser.h"
+#include "s2/s2edge_distances.h"
+#include "s2/s2latlng.h"
+#include "s2/s2latlng_rect.h"
+#include "s2/s2measures.h"
+#include "s2/s2metrics.h"
+
+using S2::internal::kPosToIJ;
+using S2::internal::kPosToOrientation;
+using std::min;
+using std::max;
+
+// Since S2Cells are copied by value, the following assertion is a reminder
+// not to add fields unnecessarily. An S2Cell currently consists of 43 data
+// bytes, one vtable pointer, plus alignment overhead. This works out to 48
+// bytes on 32 bit architectures and 56 bytes on 64 bit architectures.
+//
+// The expression below rounds up (43 + sizeof(void*)) to the nearest
+// multiple of sizeof(void*).
+static_assert(sizeof(S2Cell) <= ((43+2*sizeof(void*)-1) & -sizeof(void*)),
+ "S2Cell is getting bloated");
+
+S2Cell::S2Cell(S2CellId id) {
+ id_ = id;
+ int ij[2], orientation;
+ face_ = id.ToFaceIJOrientation(&ij[0], &ij[1], &orientation);
+ orientation_ = orientation; // Compress int to a byte.
+ level_ = id.level();
+ uv_ = S2CellId::IJLevelToBoundUV(ij, level_);
+}
+
+S2Point S2Cell::GetVertexRaw(int k) const {
+ return S2::FaceUVtoXYZ(face_, uv_.GetVertex(k));
+}
+
+S2Point S2Cell::GetEdgeRaw(int k) const {
+ switch (k & 3) {
+ case 0: return S2::GetVNorm(face_, uv_[1][0]); // Bottom
+ case 1: return S2::GetUNorm(face_, uv_[0][1]); // Right
+ case 2: return -S2::GetVNorm(face_, uv_[1][1]); // Top
+ default: return -S2::GetUNorm(face_, uv_[0][0]); // Left
+ }
+}
+
+bool S2Cell::Subdivide(S2Cell children[4]) const {
+ // This function is equivalent to just iterating over the child cell ids
+ // and calling the S2Cell constructor, but it is about 2.5 times faster.
+
+ if (id_.is_leaf()) return false;
+
+ // Compute the cell midpoint in uv-space.
+ R2Point uv_mid = id_.GetCenterUV();
+
+ // Create four children with the appropriate bounds.
+ S2CellId id = id_.child_begin();
+ for (int pos = 0; pos < 4; ++pos, id = id.next()) {
+ S2Cell *child = &children[pos];
+ child->face_ = face_;
+ child->level_ = level_ + 1;
+ child->orientation_ = orientation_ ^ kPosToOrientation[pos];
+ child->id_ = id;
+ // We want to split the cell in half in "u" and "v". To decide which
+ // side to set equal to the midpoint value, we look at cell's (i,j)
+ // position within its parent. The index for "i" is in bit 1 of ij.
+ int ij = kPosToIJ[orientation_][pos];
+ int i = ij >> 1;
+ int j = ij & 1;
+ child->uv_[0][i] = uv_[0][i];
+ child->uv_[0][1-i] = uv_mid[0];
+ child->uv_[1][j] = uv_[1][j];
+ child->uv_[1][1-j] = uv_mid[1];
+ }
+ return true;
+}
+
+S2Point S2Cell::GetCenterRaw() const {
+ return id_.ToPointRaw();
+}
+
+double S2Cell::AverageArea(int level) {
+ return S2::kAvgArea.GetValue(level);
+}
+
+double S2Cell::ApproxArea() const {
+ // All cells at the first two levels have the same area.
+ if (level_ < 2) return AverageArea(level_);
+
+ // First, compute the approximate area of the cell when projected
+ // perpendicular to its normal. The cross product of its diagonals gives
+ // the normal, and the length of the normal is twice the projected area.
+ double flat_area = 0.5 * (GetVertex(2) - GetVertex(0)).
+ CrossProd(GetVertex(3) - GetVertex(1)).Norm();
+
+ // Now, compensate for the curvature of the cell surface by pretending
+ // that the cell is shaped like a spherical cap. The ratio of the
+ // area of a spherical cap to the area of its projected disc turns out
+ // to be 2 / (1 + sqrt(1 - r*r)) where "r" is the radius of the disc.
+ // For example, when r=0 the ratio is 1, and when r=1 the ratio is 2.
+ // Here we set Pi*r*r == flat_area to find the equivalent disc.
+ return flat_area * 2 / (1 + sqrt(1 - min(M_1_PI * flat_area, 1.0)));
+}
+
+double S2Cell::ExactArea() const {
+ // There is a straightforward mathematical formula for the exact surface
+ // area (based on 4 calls to asin), but as the cell size gets small this
+ // formula has too much cancellation error. So instead we compute the area
+ // as the sum of two triangles (which is very accurate at all cell levels).
+ S2Point v0 = GetVertex(0);
+ S2Point v1 = GetVertex(1);
+ S2Point v2 = GetVertex(2);
+ S2Point v3 = GetVertex(3);
+ return S2::Area(v0, v1, v2) + S2::Area(v0, v2, v3);
+}
+
+S2Cell* S2Cell::Clone() const {
+ return new S2Cell(*this);
+}
+
+S2Cap S2Cell::GetCapBound() const {
+ // Use the cell center in (u,v)-space as the cap axis. This vector is
+ // very close to GetCenter() and faster to compute. Neither one of these
+ // vectors yields the bounding cap with minimal surface area, but they
+ // are both pretty close.
+ //
+ // It's possible to show that the two vertices that are furthest from
+ // the (u,v)-origin never determine the maximum cap size (this is a
+ // possible future optimization).
+
+ S2Point center = S2::FaceUVtoXYZ(face_, uv_.GetCenter()).Normalize();
+ S2Cap cap = S2Cap::FromPoint(center);
+ for (int k = 0; k < 4; ++k) {
+ cap.AddPoint(GetVertex(k));
+ }
+ return cap;
+}
+
+inline double S2Cell::GetLatitude(int i, int j) const {
+ S2Point p = S2::FaceUVtoXYZ(face_, uv_[0][i], uv_[1][j]);
+ return S2LatLng::Latitude(p).radians();
+}
+
+inline double S2Cell::GetLongitude(int i, int j) const {
+ S2Point p = S2::FaceUVtoXYZ(face_, uv_[0][i], uv_[1][j]);
+ return S2LatLng::Longitude(p).radians();
+}
+
+S2LatLngRect S2Cell::GetRectBound() const {
+ if (level_ > 0) {
+ // Except for cells at level 0, the latitude and longitude extremes are
+ // attained at the vertices. Furthermore, the latitude range is
+ // determined by one pair of diagonally opposite vertices and the
+ // longitude range is determined by the other pair.
+ //
+ // We first determine which corner (i,j) of the cell has the largest
+ // absolute latitude. To maximize latitude, we want to find the point in
+ // the cell that has the largest absolute z-coordinate and the smallest
+ // absolute x- and y-coordinates. To do this we look at each coordinate
+ // (u and v), and determine whether we want to minimize or maximize that
+ // coordinate based on the axis direction and the cell's (u,v) quadrant.
+ double u = uv_[0][0] + uv_[0][1];
+ double v = uv_[1][0] + uv_[1][1];
+ int i = S2::GetUAxis(face_)[2] == 0 ? (u < 0) : (u > 0);
+ int j = S2::GetVAxis(face_)[2] == 0 ? (v < 0) : (v > 0);
+ R1Interval lat = R1Interval::FromPointPair(GetLatitude(i, j),
+ GetLatitude(1-i, 1-j));
+ S1Interval lng = S1Interval::FromPointPair(GetLongitude(i, 1-j),
+ GetLongitude(1-i, j));
+
+ // We grow the bounds slightly to make sure that the bounding rectangle
+ // contains S2LatLng(P) for any point P inside the loop L defined by the
+ // four *normalized* vertices. Note that normalization of a vector can
+ // change its direction by up to 0.5 * DBL_EPSILON radians, and it is not
+ // enough just to add Normalize() calls to the code above because the
+ // latitude/longitude ranges are not necessarily determined by diagonally
+ // opposite vertex pairs after normalization.
+ //
+ // We would like to bound the amount by which the latitude/longitude of a
+ // contained point P can exceed the bounds computed above. In the case of
+ // longitude, the normalization error can change the direction of rounding
+ // leading to a maximum difference in longitude of 2 * DBL_EPSILON. In
+ // the case of latitude, the normalization error can shift the latitude by
+ // up to 0.5 * DBL_EPSILON and the other sources of error can cause the
+ // two latitudes to differ by up to another 1.5 * DBL_EPSILON, which also
+ // leads to a maximum difference of 2 * DBL_EPSILON.
+ return S2LatLngRect(lat, lng).
+ Expanded(S2LatLng::FromRadians(2 * DBL_EPSILON, 2 * DBL_EPSILON)).
+ PolarClosure();
+ }
+
+ // The 4 cells around the equator extend to +/-45 degrees latitude at the
+ // midpoints of their top and bottom edges. The two cells covering the
+ // poles extend down to +/-35.26 degrees at their vertices. The maximum
+ // error in this calculation is 0.5 * DBL_EPSILON.
+ static const double kPoleMinLat = asin(sqrt(1./3)) - 0.5 * DBL_EPSILON;
+
+ // The face centers are the +X, +Y, +Z, -X, -Y, -Z axes in that order.
+ S2_DCHECK_EQ(((face_ < 3) ? 1 : -1), S2::GetNorm(face_)[face_ % 3]);
+
+ S2LatLngRect bound;
+ switch (face_) {
+ case 0:
+ bound = S2LatLngRect(R1Interval(-M_PI_4, M_PI_4),
+ S1Interval(-M_PI_4, M_PI_4));
+ break;
+ case 1:
+ bound = S2LatLngRect(R1Interval(-M_PI_4, M_PI_4),
+ S1Interval(M_PI_4, 3*M_PI_4));
+ break;
+ case 2:
+ bound = S2LatLngRect(R1Interval(kPoleMinLat, M_PI_2),
+ S1Interval::Full());
+ break;
+ case 3:
+ bound = S2LatLngRect(R1Interval(-M_PI_4, M_PI_4),
+ S1Interval(3*M_PI_4, -3*M_PI_4));
+ break;
+ case 4:
+ bound = S2LatLngRect(R1Interval(-M_PI_4, M_PI_4),
+ S1Interval(-3*M_PI_4, -M_PI_4));
+ break;
+ default:
+ bound = S2LatLngRect(R1Interval(-M_PI_2, -kPoleMinLat),
+ S1Interval::Full());
+ break;
+ }
+ // Finally, we expand the bound to account for the error when a point P is
+ // converted to an S2LatLng to test for containment. (The bound should be
+ // large enough so that it contains the computed S2LatLng of any contained
+ // point, not just the infinite-precision version.) We don't need to expand
+ // longitude because longitude is calculated via a single call to atan2(),
+ // which is guaranteed to be semi-monotonic. (In fact the Gnu implementation
+ // is also correctly rounded, but we don't even need that here.)
+ return bound.Expanded(S2LatLng::FromRadians(DBL_EPSILON, 0));
+}
+
+bool S2Cell::MayIntersect(const S2Cell& cell) const {
+ return id_.intersects(cell.id_);
+}
+
+bool S2Cell::Contains(const S2Cell& cell) const {
+ return id_.contains(cell.id_);
+}
+
+bool S2Cell::Contains(const S2Point& p) const {
+ // We can't just call XYZtoFaceUV, because for points that lie on the
+ // boundary between two faces (i.e. u or v is +1/-1) we need to return
+ // true for both adjacent cells.
+ R2Point uv;
+ if (!S2::FaceXYZtoUV(face_, p, &uv)) return false;
+
+ // Expand the (u,v) bound to ensure that
+ //
+ // S2Cell(S2CellId(p)).Contains(p)
+ //
+ // is always true. To do this, we need to account for the error when
+ // converting from (u,v) coordinates to (s,t) coordinates. At least in the
+ // case of S2_QUADRATIC_PROJECTION, the total error is at most DBL_EPSILON.
+ return uv_.Expanded(DBL_EPSILON).Contains(uv);
+}
+
+void S2Cell::Encode(Encoder* const encoder) const {
+ id_.Encode(encoder);
+}
+
+bool S2Cell::Decode(Decoder* const decoder) {
+ S2CellId id;
+ if (!id.Decode(decoder)) return false;
+ this->~S2Cell();
+ new (this) S2Cell(id);
+ return true;
+}
+
+// Return the squared chord distance from point P to corner vertex (i,j).
+inline S1ChordAngle S2Cell::VertexChordDist(
+ const S2Point& p, int i, int j) const {
+ S2Point vertex = S2Point(uv_[0][i], uv_[1][j], 1).Normalize();
+ return S1ChordAngle(p, vertex);
+}
+
+// Given a point P and either the lower or upper edge of the S2Cell (specified
+// by setting "v_end" to 0 or 1 respectively), return true if P is closer to
+// the interior of that edge than it is to either endpoint.
+bool S2Cell::UEdgeIsClosest(const S2Point& p, int v_end) const {
+ double u0 = uv_[0][0], u1 = uv_[0][1], v = uv_[1][v_end];
+ // These are the normals to the planes that are perpendicular to the edge
+ // and pass through one of its two endpoints.
+ S2Point dir0(v * v + 1, -u0 * v, -u0);
+ S2Point dir1(v * v + 1, -u1 * v, -u1);
+ return p.DotProd(dir0) > 0 && p.DotProd(dir1) < 0;
+}
+
+// Given a point P and either the left or right edge of the S2Cell (specified
+// by setting "u_end" to 0 or 1 respectively), return true if P is closer to
+// the interior of that edge than it is to either endpoint.
+bool S2Cell::VEdgeIsClosest(const S2Point& p, int u_end) const {
+ double v0 = uv_[1][0], v1 = uv_[1][1], u = uv_[0][u_end];
+ // See comments above.
+ S2Point dir0(-u * v0, u * u + 1, -v0);
+ S2Point dir1(-u * v1, u * u + 1, -v1);
+ return p.DotProd(dir0) > 0 && p.DotProd(dir1) < 0;
+}
+
+// Given the dot product of a point P with the normal of a u- or v-edge at the
+// given coordinate value, return the distance from P to that edge.
+inline static S1ChordAngle EdgeDistance(double dirIJ, double uv) {
+ // Let P by the target point and let R be the closest point on the given
+ // edge AB. The desired distance PR can be expressed as PR^2 = PQ^2 + QR^2
+ // where Q is the point P projected onto the plane through the great circle
+ // through AB. We can compute the distance PQ^2 perpendicular to the plane
+ // from "dirIJ" (the dot product of the target point P with the edge
+ // normal) and the squared length the edge normal (1 + uv**2).
+ double pq2 = (dirIJ * dirIJ) / (1 + uv * uv);
+
+ // We can compute the distance QR as (1 - OQ) where O is the sphere origin,
+ // and we can compute OQ^2 = 1 - PQ^2 using the Pythagorean theorem.
+ // (This calculation loses accuracy as angle POQ approaches Pi/2.)
+ double qr = 1 - sqrt(1 - pq2);
+ return S1ChordAngle::FromLength2(pq2 + qr * qr);
+}
+
+S1ChordAngle S2Cell::GetDistanceInternal(const S2Point& target_xyz,
+ bool to_interior) const {
+ // All calculations are done in the (u,v,w) coordinates of this cell's face.
+ S2Point target = S2::FaceXYZtoUVW(face_, target_xyz);
+
+ // Compute dot products with all four upward or rightward-facing edge
+ // normals. "dirIJ" is the dot product for the edge corresponding to axis
+ // I, endpoint J. For example, dir01 is the right edge of the S2Cell
+ // (corresponding to the upper endpoint of the u-axis).
+ double dir00 = target[0] - target[2] * uv_[0][0];
+ double dir01 = target[0] - target[2] * uv_[0][1];
+ double dir10 = target[1] - target[2] * uv_[1][0];
+ double dir11 = target[1] - target[2] * uv_[1][1];
+ bool inside = true;
+ if (dir00 < 0) {
+ inside = false; // Target is to the left of the cell
+ if (VEdgeIsClosest(target, 0)) return EdgeDistance(-dir00, uv_[0][0]);
+ }
+ if (dir01 > 0) {
+ inside = false; // Target is to the right of the cell
+ if (VEdgeIsClosest(target, 1)) return EdgeDistance(dir01, uv_[0][1]);
+ }
+ if (dir10 < 0) {
+ inside = false; // Target is below the cell
+ if (UEdgeIsClosest(target, 0)) return EdgeDistance(-dir10, uv_[1][0]);
+ }
+ if (dir11 > 0) {
+ inside = false; // Target is above the cell
+ if (UEdgeIsClosest(target, 1)) return EdgeDistance(dir11, uv_[1][1]);
+ }
+ if (inside) {
+ if (to_interior) return S1ChordAngle::Zero();
+ // Although you might think of S2Cells as rectangles, they are actually
+ // arbitrary quadrilaterals after they are projected onto the sphere.
+ // Therefore the simplest approach is just to find the minimum distance to
+ // any of the four edges.
+ return min(min(EdgeDistance(-dir00, uv_[0][0]),
+ EdgeDistance(dir01, uv_[0][1])),
+ min(EdgeDistance(-dir10, uv_[1][0]),
+ EdgeDistance(dir11, uv_[1][1])));
+ }
+ // Otherwise, the closest point is one of the four cell vertices. Note that
+ // it is *not* trivial to narrow down the candidates based on the edge sign
+ // tests above, because (1) the edges don't meet at right angles and (2)
+ // there are points on the far side of the sphere that are both above *and*
+ // below the cell, etc.
+ return min(min(VertexChordDist(target, 0, 0),
+ VertexChordDist(target, 1, 0)),
+ min(VertexChordDist(target, 0, 1),
+ VertexChordDist(target, 1, 1)));
+}
+
+S1ChordAngle S2Cell::GetDistance(const S2Point& target) const {
+ return GetDistanceInternal(target, true /*to_interior*/);
+}
+
+S1ChordAngle S2Cell::GetBoundaryDistance(const S2Point& target) const {
+ return GetDistanceInternal(target, false /*to_interior*/);
+}
+
+S1ChordAngle S2Cell::GetMaxDistance(const S2Point& target) const {
+ // First check the 4 cell vertices. If all are within the hemisphere
+ // centered around target, the max distance will be to one of these vertices.
+ S2Point target_uvw = S2::FaceXYZtoUVW(face_, target);
+ S1ChordAngle max_dist = max(max(VertexChordDist(target_uvw, 0, 0),
+ VertexChordDist(target_uvw, 1, 0)),
+ max(VertexChordDist(target_uvw, 0, 1),
+ VertexChordDist(target_uvw, 1, 1)));
+
+ if (max_dist <= S1ChordAngle::Right()) {
+ return max_dist;
+ }
+
+ // Otherwise, find the minimum distance d_min to the antipodal point and the
+ // maximum distance will be Pi - d_min.
+ return S1ChordAngle::Straight() - GetDistance(-target);
+}
+
+S1ChordAngle S2Cell::GetDistance(const S2Point& a, const S2Point& b) const {
+ // Possible optimizations:
+ // - Currently the (cell vertex, edge endpoint) distances are computed
+ // twice each, and the length of AB is computed 4 times.
+ // - To fix this, refactor GetDistance(target) so that it skips calculating
+ // the distance to each cell vertex. Instead, compute the cell vertices
+ // and distances in this function, and add a low-level UpdateMinDistance
+ // that allows the XA, XB, and AB distances to be passed in.
+ // - It might also be more efficient to do all calculations in UVW-space,
+ // since this would involve transforming 2 points rather than 4.
+
+ // First, check the minimum distance to the edge endpoints A and B.
+ // (This also detects whether either endpoint is inside the cell.)
+ S1ChordAngle min_dist = min(GetDistance(a), GetDistance(b));
+ if (min_dist == S1ChordAngle::Zero()) return min_dist;
+
+ // Otherwise, check whether the edge crosses the cell boundary.
+ // Note that S2EdgeCrosser needs pointers to vertices.
+ S2Point v[4];
+ for (int i = 0; i < 4; ++i) {
+ v[i] = GetVertex(i);
+ }
+ S2EdgeCrosser crosser(&a, &b, &v[3]);
+ for (int i = 0; i < 4; ++i) {
+ if (crosser.CrossingSign(&v[i]) >= 0) {
+ return S1ChordAngle::Zero();
+ }
+ }
+ // Finally, check whether the minimum distance occurs between a cell vertex
+ // and the interior of the edge AB. (Some of this work is redundant, since
+ // it also checks the distance to the endpoints A and B again.)
+ //
+ // Note that we don't need to check the distance from the interior of AB to
+ // the interior of a cell edge, because the only way that this distance can
+ // be minimal is if the two edges cross (already checked above).
+ for (int i = 0; i < 4; ++i) {
+ S2::UpdateMinDistance(v[i], a, b, &min_dist);
+ }
+ return min_dist;
+}
+
+S1ChordAngle S2Cell::GetMaxDistance(const S2Point& a, const S2Point& b) const {
+ // If the maximum distance from both endpoints to the cell is less than Pi/2
+ // then the maximum distance from the edge to the cell is the maximum of the
+ // two endpoint distances.
+ S1ChordAngle max_dist = max(GetMaxDistance(a), GetMaxDistance(b));
+ if (max_dist <= S1ChordAngle::Right()) {
+ return max_dist;
+ }
+
+ return S1ChordAngle::Straight() - GetDistance(-a, -b);
+}
+
+S1ChordAngle S2Cell::GetDistance(const S2Cell& target) const {
+ // If the cells intersect, the distance is zero. We use the (u,v) ranges
+ // rather S2CellId::intersects() so that cells that share a partial edge or
+ // corner are considered to intersect.
+ if (face_ == target.face_ && uv_.Intersects(target.uv_)) {
+ return S1ChordAngle::Zero();
+ }
+
+ // Otherwise, the minimum distance always occurs between a vertex of one
+ // cell and an edge of the other cell (including the edge endpoints). This
+ // represents a total of 32 possible (vertex, edge) pairs.
+ //
+ // TODO(ericv): This could be optimized to be at least 5x faster by pruning
+ // the set of possible closest vertex/edge pairs using the faces and (u,v)
+ // ranges of both cells.
+ S2Point va[4], vb[4];
+ for (int i = 0; i < 4; ++i) {
+ va[i] = GetVertex(i);
+ vb[i] = target.GetVertex(i);
+ }
+ S1ChordAngle min_dist = S1ChordAngle::Infinity();
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ S2::UpdateMinDistance(va[i], vb[j], vb[(j + 1) & 3], &min_dist);
+ S2::UpdateMinDistance(vb[i], va[j], va[(j + 1) & 3], &min_dist);
+ }
+ }
+ return min_dist;
+}
+
+inline static int OppositeFace(int face) {
+ return face >= 3 ? face - 3 : face + 3;
+}
+
+// The antipodal UV is the transpose of the original UV, interpreted within
+// the opposite face.
+inline static R2Rect OppositeUV(const R2Rect& uv) {
+ return R2Rect(uv[1], uv[0]);
+}
+
+S1ChordAngle S2Cell::GetMaxDistance(const S2Cell& target) const {
+ // Need to check the antipodal target for intersection with the cell. If it
+ // intersects, the distance is S1ChordAngle::Straight().
+ if (face_ == OppositeFace(target.face_) &&
+ uv_.Intersects(OppositeUV(target.uv_))) {
+ return S1ChordAngle::Straight();
+ }
+
+ // Otherwise, the maximum distance always occurs between a vertex of one
+ // cell and an edge of the other cell (including the edge endpoints). This
+ // represents a total of 32 possible (vertex, edge) pairs.
+ //
+ // TODO(user): When the maximum distance is at most Pi/2, the maximum is
+ // always attained between a pair of vertices, and this could be made much
+ // faster by testing each vertex pair once rather than the current 4 times.
+ S2Point va[4], vb[4];
+ for (int i = 0; i < 4; ++i) {
+ va[i] = GetVertex(i);
+ vb[i] = target.GetVertex(i);
+ }
+ S1ChordAngle max_dist = S1ChordAngle::Negative();
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ S2::UpdateMaxDistance(va[i], vb[j], vb[(j + 1) & 3], &max_dist);
+ S2::UpdateMaxDistance(vb[i], va[j], va[(j + 1) & 3], &max_dist);
+ }
+ }
+ return max_dist;
+}
+
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2cell_id.h"
+
+#include <algorithm>
+#include <cfloat>
+#include <cmath>
+#include <cstring>
+#include <iosfwd>
+#include <mutex>
+#include <vector>
+
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+#include "s2/r1interval.h"
+#include "s2/s2coords.h"
+#include "s2/s2latlng.h"
+#include "s2/third_party/absl/base/casts.h"
+#include "s2/third_party/absl/strings/str_cat.h"
+
+using absl::StrCat;
+using S2::internal::kSwapMask;
+using S2::internal::kInvertMask;
+using S2::internal::kPosToIJ;
+using S2::internal::kPosToOrientation;
+using std::fabs;
+using std::max;
+using std::min;
+using std::vector;
+
+// The following lookup tables are used to convert efficiently between an
+// (i,j) cell index and the corresponding position along the Hilbert curve.
+// "lookup_pos" maps 4 bits of "i", 4 bits of "j", and 2 bits representing the
+// orientation of the current cell into 8 bits representing the order in which
+// that subcell is visited by the Hilbert curve, plus 2 bits indicating the
+// new orientation of the Hilbert curve within that subcell. (Cell
+// orientations are represented as combination of kSwapMask and kInvertMask.)
+//
+// "lookup_ij" is an inverted table used for mapping in the opposite
+// direction.
+//
+// We also experimented with looking up 16 bits at a time (14 bits of position
+// plus 2 of orientation) but found that smaller lookup tables gave better
+// performance. (2KB fits easily in the primary cache.)
+
+
+// Values for these constants are *declared* in the *.h file. Even though
+// the declaration specifies a value for the constant, that declaration
+// is not a *definition* of storage for the value. Because the values are
+// supplied in the declaration, we don't need the values here. Failing to
+// define storage causes link errors for any code that tries to take the
+// address of one of these values.
+const int S2CellId::kFaceBits;
+const int S2CellId::kNumFaces;
+const int S2CellId::kMaxLevel;
+const int S2CellId::kPosBits;
+const int S2CellId::kMaxSize;
+
+static const int kLookupBits = 4;
+static uint16 lookup_pos[1 << (2 * kLookupBits + 2)];
+static uint16 lookup_ij[1 << (2 * kLookupBits + 2)];
+
+static void InitLookupCell(int level, int i, int j, int orig_orientation,
+ int pos, int orientation) {
+ if (level == kLookupBits) {
+ int ij = (i << kLookupBits) + j;
+ lookup_pos[(ij << 2) + orig_orientation] = (pos << 2) + orientation;
+ lookup_ij[(pos << 2) + orig_orientation] = (ij << 2) + orientation;
+ } else {
+ level++;
+ i <<= 1;
+ j <<= 1;
+ pos <<= 2;
+ const int* r = kPosToIJ[orientation];
+ InitLookupCell(level, i + (r[0] >> 1), j + (r[0] & 1), orig_orientation,
+ pos, orientation ^ kPosToOrientation[0]);
+ InitLookupCell(level, i + (r[1] >> 1), j + (r[1] & 1), orig_orientation,
+ pos + 1, orientation ^ kPosToOrientation[1]);
+ InitLookupCell(level, i + (r[2] >> 1), j + (r[2] & 1), orig_orientation,
+ pos + 2, orientation ^ kPosToOrientation[2]);
+ InitLookupCell(level, i + (r[3] >> 1), j + (r[3] & 1), orig_orientation,
+ pos + 3, orientation ^ kPosToOrientation[3]);
+ }
+}
+
+static std::once_flag flag;
+inline static void MaybeInit() {
+ std::call_once(flag, []{
+ InitLookupCell(0, 0, 0, 0, 0, 0);
+ InitLookupCell(0, 0, 0, kSwapMask, 0, kSwapMask);
+ InitLookupCell(0, 0, 0, kInvertMask, 0, kInvertMask);
+ InitLookupCell(0, 0, 0, kSwapMask|kInvertMask, 0, kSwapMask|kInvertMask);
+ });
+}
+
+S2CellId S2CellId::advance(int64 steps) const {
+ if (steps == 0) return *this;
+
+ // We clamp the number of steps if necessary to ensure that we do not
+ // advance past the End() or before the Begin() of this level. Note that
+ // min_steps and max_steps always fit in a signed 64-bit integer.
+
+ int step_shift = 2 * (kMaxLevel - level()) + 1;
+ if (steps < 0) {
+ int64 min_steps = -static_cast<int64>(id_ >> step_shift);
+ if (steps < min_steps) steps = min_steps;
+ } else {
+ int64 max_steps = (kWrapOffset + lsb() - id_) >> step_shift;
+ if (steps > max_steps) steps = max_steps;
+ }
+ // If steps is negative, then shifting it left has undefined behavior.
+ // Cast to uint64 for a 2's complement answer.
+ return S2CellId(id_ + (static_cast<uint64>(steps) << step_shift));
+}
+
+int64 S2CellId::distance_from_begin() const {
+ const int step_shift = 2 * (kMaxLevel - level()) + 1;
+ return id_ >> step_shift;
+}
+
+S2CellId S2CellId::advance_wrap(int64 steps) const {
+ S2_DCHECK(is_valid());
+ if (steps == 0) return *this;
+
+ int step_shift = 2 * (kMaxLevel - level()) + 1;
+ if (steps < 0) {
+ int64 min_steps = -static_cast<int64>(id_ >> step_shift);
+ if (steps < min_steps) {
+ int64 step_wrap = kWrapOffset >> step_shift;
+ steps %= step_wrap;
+ if (steps < min_steps) steps += step_wrap;
+ }
+ } else {
+ // Unlike advance(), we don't want to return End(level).
+ int64 max_steps = (kWrapOffset - id_) >> step_shift;
+ if (steps > max_steps) {
+ int64 step_wrap = kWrapOffset >> step_shift;
+ steps %= step_wrap;
+ if (steps > max_steps) steps -= step_wrap;
+ }
+ }
+ return S2CellId(id_ + (static_cast<uint64>(steps) << step_shift));
+}
+
+S2CellId S2CellId::maximum_tile(const S2CellId limit) const {
+ S2CellId id = *this;
+ S2CellId start = id.range_min();
+ if (start >= limit.range_min()) return limit;
+
+ if (id.range_max() >= limit) {
+ // The cell is too large. Shrink it. Note that when generating coverings
+ // of S2CellId ranges, this loop usually executes only once. Also because
+ // id.range_min() < limit.range_min(), we will always exit the loop by the
+ // time we reach a leaf cell.
+ do { id = id.child(0); } while (id.range_max() >= limit);
+ return id;
+ }
+ // The cell may be too small. Grow it if necessary. Note that generally
+ // this loop only iterates once.
+ while (!id.is_face()) {
+ S2CellId parent = id.parent();
+ if (parent.range_min() != start || parent.range_max() >= limit) break;
+ id = parent;
+ }
+ return id;
+}
+
+int S2CellId::GetCommonAncestorLevel(S2CellId other) const {
+ // Basically we find the first bit position at which the two S2CellIds
+ // differ and convert that to a level. The max() below is necessary for the
+ // case where one S2CellId is a descendant of the other.
+ uint64 bits = max(id() ^ other.id(), max(lsb(), other.lsb()));
+ S2_DCHECK_NE(bits, 0); // Because lsb() is non-zero.
+
+ // Compute the position of the most significant bit, and then map the bit
+ // position as follows:
+ // {0} -> 30, {1,2} -> 29, {3,4} -> 28, ... , {59,60} -> 0, {61,62,63} -> -1.
+ return max(60 - Bits::FindMSBSetNonZero64(bits), -1) >> 1;
+}
+
+// Print the num_digits low order hex digits.
+static string HexFormatString(uint64 val, size_t num_digits) {
+ string result(num_digits, ' ');
+ for (; num_digits--; val >>= 4)
+ result[num_digits] = "0123456789abcdef"[val & 0xF];
+ return result;
+}
+
+string S2CellId::ToToken() const {
+ // Simple implementation: print the id in hex without trailing zeros.
+ // Using hex has the advantage that the tokens are case-insensitive, all
+ // characters are alphanumeric, no characters require any special escaping
+ // in queries for most indexing systems, and it's easy to compare cell
+ // tokens against the feature ids of the corresponding features.
+ //
+ // Using base 64 would produce slightly shorter tokens, but for typical cell
+ // sizes used during indexing (up to level 15 or so) the average savings
+ // would be less than 2 bytes per cell which doesn't seem worth it.
+
+ // "0" with trailing 0s stripped is the empty string, which is not a
+ // reasonable token. Encode as "X".
+ if (id_ == 0) return "X";
+ const size_t num_zero_digits = Bits::FindLSBSetNonZero64(id_) / 4;
+ return HexFormatString(id_ >> (4 * num_zero_digits), 16 - num_zero_digits);
+}
+
+S2CellId S2CellId::FromToken(const char* token, size_t length) {
+ if (length > 16) return S2CellId::None();
+ uint64 id = 0;
+ for (int i = 0, pos = 60; i < length; ++i, pos -= 4) {
+ uint64 d;
+ if ('0' <= token[i] && token[i] <= '9') {
+ d = token[i] - '0';
+ } else if ('a' <= token[i] && token[i] <= 'f') {
+ d = token[i] - 'a' + 10;
+ } else if ('A' <= token[i] && token[i] <= 'F') {
+ d = token[i] - 'A' + 10;
+ } else {
+ return S2CellId::None();
+ }
+ id |= d << pos;
+ }
+ return S2CellId(id);
+}
+
+S2CellId S2CellId::FromToken(const string& token) {
+ return FromToken(token.data(), token.size());
+}
+
+void S2CellId::Encode(Encoder* const encoder) const {
+ encoder->Ensure(sizeof(uint64)); // A single uint64.
+ encoder->put64(id_);
+}
+
+bool S2CellId::Decode(Decoder* const decoder) {
+ if (decoder->avail() < sizeof(uint64)) return false;
+ id_ = decoder->get64();
+ return true;
+}
+
+S2CellId S2CellId::FromFaceIJ(int face, int i, int j) {
+ // Initialization if not done yet
+ MaybeInit();
+
+ // Optimization notes:
+ // - Non-overlapping bit fields can be combined with either "+" or "|".
+ // Generally "+" seems to produce better code, but not always.
+
+ // Note that this value gets shifted one bit to the left at the end
+ // of the function.
+ uint64 n = absl::implicit_cast<uint64>(face) << (kPosBits - 1);
+
+ // Alternating faces have opposite Hilbert curve orientations; this
+ // is necessary in order for all faces to have a right-handed
+ // coordinate system.
+ uint64 bits = (face & kSwapMask);
+
+ // Each iteration maps 4 bits of "i" and "j" into 8 bits of the Hilbert
+ // curve position. The lookup table transforms a 10-bit key of the form
+ // "iiiijjjjoo" to a 10-bit value of the form "ppppppppoo", where the
+ // letters [ijpo] denote bits of "i", "j", Hilbert curve position, and
+ // Hilbert curve orientation respectively.
+#define GET_BITS(k) do { \
+ const int mask = (1 << kLookupBits) - 1; \
+ bits += ((i >> (k * kLookupBits)) & mask) << (kLookupBits + 2); \
+ bits += ((j >> (k * kLookupBits)) & mask) << 2; \
+ bits = lookup_pos[bits]; \
+ n |= (bits >> 2) << (k * 2 * kLookupBits); \
+ bits &= (kSwapMask | kInvertMask); \
+ } while (0)
+
+ GET_BITS(7);
+ GET_BITS(6);
+ GET_BITS(5);
+ GET_BITS(4);
+ GET_BITS(3);
+ GET_BITS(2);
+ GET_BITS(1);
+ GET_BITS(0);
+#undef GET_BITS
+
+ return S2CellId(n * 2 + 1);
+}
+
+S2CellId::S2CellId(const S2Point& p) {
+ double u, v;
+ int face = S2::XYZtoFaceUV(p, &u, &v);
+ int i = S2::STtoIJ(S2::UVtoST(u));
+ int j = S2::STtoIJ(S2::UVtoST(v));
+ id_ = FromFaceIJ(face, i, j).id();
+}
+
+S2CellId::S2CellId(const S2LatLng& ll)
+ : S2CellId(ll.ToPoint()) {
+}
+
+int S2CellId::ToFaceIJOrientation(int* pi, int* pj, int* orientation) const {
+ // Initialization if not done yet
+ MaybeInit();
+
+ int i = 0, j = 0;
+ int face = this->face();
+ int bits = (face & kSwapMask);
+
+ // Each iteration maps 8 bits of the Hilbert curve position into
+ // 4 bits of "i" and "j". The lookup table transforms a key of the
+ // form "ppppppppoo" to a value of the form "iiiijjjjoo", where the
+ // letters [ijpo] represents bits of "i", "j", the Hilbert curve
+ // position, and the Hilbert curve orientation respectively.
+ //
+ // On the first iteration we need to be careful to clear out the bits
+ // representing the cube face.
+#define GET_BITS(k) do { \
+ const int nbits = (k == 7) ? (kMaxLevel - 7 * kLookupBits) : kLookupBits; \
+ bits += (static_cast<int>(id_ >> (k * 2 * kLookupBits + 1)) \
+ & ((1 << (2 * nbits)) - 1)) << 2; \
+ bits = lookup_ij[bits]; \
+ i += (bits >> (kLookupBits + 2)) << (k * kLookupBits); \
+ j += ((bits >> 2) & ((1 << kLookupBits) - 1)) << (k * kLookupBits); \
+ bits &= (kSwapMask | kInvertMask); \
+ } while (0)
+
+ GET_BITS(7);
+ GET_BITS(6);
+ GET_BITS(5);
+ GET_BITS(4);
+ GET_BITS(3);
+ GET_BITS(2);
+ GET_BITS(1);
+ GET_BITS(0);
+#undef GET_BITS
+
+ *pi = i;
+ *pj = j;
+
+ if (orientation != nullptr) {
+ // The position of a non-leaf cell at level "n" consists of a prefix of
+ // 2*n bits that identifies the cell, followed by a suffix of
+ // 2*(kMaxLevel-n)+1 bits of the form 10*. If n==kMaxLevel, the suffix is
+ // just "1" and has no effect. Otherwise, it consists of "10", followed
+ // by (kMaxLevel-n-1) repetitions of "00", followed by "0". The "10" has
+ // no effect, while each occurrence of "00" has the effect of reversing
+ // the kSwapMask bit.
+ S2_DCHECK_EQ(0, kPosToOrientation[2]);
+ S2_DCHECK_EQ(kSwapMask, kPosToOrientation[0]);
+ if (lsb() & 0x1111111111111110ULL) {
+ bits ^= kSwapMask;
+ }
+ *orientation = bits;
+ }
+ return face;
+}
+
+S2Point S2CellId::ToPointRaw() const {
+ int si, ti;
+ int face = GetCenterSiTi(&si, &ti);
+ return S2::FaceSiTitoXYZ(face, si, ti);
+}
+
+S2LatLng S2CellId::ToLatLng() const {
+ return S2LatLng(ToPointRaw());
+}
+
+R2Point S2CellId::GetCenterST() const {
+ int si, ti;
+ GetCenterSiTi(&si, &ti);
+ return R2Point(S2::SiTitoST(si), S2::SiTitoST(ti));
+}
+
+R2Point S2CellId::GetCenterUV() const {
+ int si, ti;
+ GetCenterSiTi(&si, &ti);
+ return R2Point(S2::STtoUV(S2::SiTitoST(si)),
+ S2::STtoUV(S2::SiTitoST(ti)));
+}
+
+R2Rect S2CellId::IJLevelToBoundUV(int ij[2], int level) {
+ R2Rect bound;
+ int cell_size = GetSizeIJ(level);
+ for (int d = 0; d < 2; ++d) {
+ int ij_lo = ij[d] & -cell_size;
+ int ij_hi = ij_lo + cell_size;
+ bound[d][0] = S2::STtoUV(S2::IJtoSTMin(ij_lo));
+ bound[d][1] = S2::STtoUV(S2::IJtoSTMin(ij_hi));
+ }
+ return bound;
+}
+
+R2Rect S2CellId::GetBoundST() const {
+ double size = GetSizeST();
+ return R2Rect::FromCenterSize(GetCenterST(), R2Point(size, size));
+}
+
+R2Rect S2CellId::GetBoundUV() const {
+ int ij[2];
+ ToFaceIJOrientation(&ij[0], &ij[1], nullptr);
+ return IJLevelToBoundUV(ij, level());
+}
+
+// This is a helper function for ExpandedByDistanceUV().
+//
+// Given an edge of the form (u,v0)-(u,v1), let max_v = max(abs(v0), abs(v1)).
+// This method returns a new u-coordinate u' such that the distance from the
+// line u=u' to the given edge (u,v0)-(u,v1) is exactly the given distance
+// (which is specified as the sine of the angle corresponding to the distance).
+static double ExpandEndpoint(double u, double max_v, double sin_dist) {
+ // This is based on solving a spherical right triangle, similar to the
+ // calculation in S2Cap::GetRectBound.
+ double sin_u_shift = sin_dist * sqrt((1 + u * u + max_v * max_v) /
+ (1 + u * u));
+ double cos_u_shift = sqrt(1 - sin_u_shift * sin_u_shift);
+ // The following is an expansion of tan(atan(u) + asin(sin_u_shift)).
+ return (cos_u_shift * u + sin_u_shift) / (cos_u_shift - sin_u_shift * u);
+}
+
+/* static */
+R2Rect S2CellId::ExpandedByDistanceUV(const R2Rect& uv, S1Angle distance) {
+ // Expand each of the four sides of the rectangle just enough to include all
+ // points within the given distance of that side. (The rectangle may be
+ // expanded by a different amount in (u,v)-space on each side.)
+ double u0 = uv[0][0], u1 = uv[0][1], v0 = uv[1][0], v1 = uv[1][1];
+ double max_u = std::max(fabs(u0), fabs(u1));
+ double max_v = std::max(fabs(v0), fabs(v1));
+ double sin_dist = sin(distance);
+ return R2Rect(R1Interval(ExpandEndpoint(u0, max_v, -sin_dist),
+ ExpandEndpoint(u1, max_v, sin_dist)),
+ R1Interval(ExpandEndpoint(v0, max_u, -sin_dist),
+ ExpandEndpoint(v1, max_u, sin_dist)));
+}
+
+S2CellId S2CellId::FromFaceIJWrap(int face, int i, int j) {
+ // Convert i and j to the coordinates of a leaf cell just beyond the
+ // boundary of this face. This prevents 32-bit overflow in the case
+ // of finding the neighbors of a face cell.
+ i = max(-1, min(kMaxSize, i));
+ j = max(-1, min(kMaxSize, j));
+
+ // We want to wrap these coordinates onto the appropriate adjacent face.
+ // The easiest way to do this is to convert the (i,j) coordinates to (x,y,z)
+ // (which yields a point outside the normal face boundary), and then call
+ // S2::XYZtoFaceUV() to project back onto the correct face.
+ //
+ // The code below converts (i,j) to (si,ti), and then (si,ti) to (u,v) using
+ // the linear projection (u=2*s-1 and v=2*t-1). (The code further below
+ // converts back using the inverse projection, s=0.5*(u+1) and t=0.5*(v+1).
+ // Any projection would work here, so we use the simplest.) We also clamp
+ // the (u,v) coordinates so that the point is barely outside the
+ // [-1,1]x[-1,1] face rectangle, since otherwise the reprojection step
+ // (which divides by the new z coordinate) might change the other
+ // coordinates enough so that we end up in the wrong leaf cell.
+ static const double kScale = 1.0 / kMaxSize;
+ static const double kLimit = 1.0 + DBL_EPSILON;
+ // The arithmetic below is designed to avoid 32-bit integer overflows.
+ S2_DCHECK_EQ(0, kMaxSize % 2);
+ double u = max(-kLimit, min(kLimit, kScale * (2 * (i - kMaxSize / 2) + 1)));
+ double v = max(-kLimit, min(kLimit, kScale * (2 * (j - kMaxSize / 2) + 1)));
+
+ // Find the leaf cell coordinates on the adjacent face, and convert
+ // them to a cell id at the appropriate level.
+ face = S2::XYZtoFaceUV(S2::FaceUVtoXYZ(face, u, v), &u, &v);
+ return FromFaceIJ(face, S2::STtoIJ(0.5*(u+1)), S2::STtoIJ(0.5*(v+1)));
+}
+
+inline S2CellId S2CellId::FromFaceIJSame(int face, int i, int j,
+ bool same_face) {
+ if (same_face)
+ return S2CellId::FromFaceIJ(face, i, j);
+ else
+ return S2CellId::FromFaceIJWrap(face, i, j);
+}
+
+void S2CellId::GetEdgeNeighbors(S2CellId neighbors[4]) const {
+ int i, j;
+ int level = this->level();
+ int size = GetSizeIJ(level);
+ int face = ToFaceIJOrientation(&i, &j, nullptr);
+
+ // Edges 0, 1, 2, 3 are in the down, right, up, left directions.
+ neighbors[0] = FromFaceIJSame(face, i, j - size, j - size >= 0)
+ .parent(level);
+ neighbors[1] = FromFaceIJSame(face, i + size, j, i + size < kMaxSize)
+ .parent(level);
+ neighbors[2] = FromFaceIJSame(face, i, j + size, j + size < kMaxSize)
+ .parent(level);
+ neighbors[3] = FromFaceIJSame(face, i - size, j, i - size >= 0)
+ .parent(level);
+}
+
+void S2CellId::AppendVertexNeighbors(int level,
+ vector<S2CellId>* output) const {
+ // "level" must be strictly less than this cell's level so that we can
+ // determine which vertex this cell is closest to.
+ S2_DCHECK_LT(level, this->level());
+ int i, j;
+ int face = ToFaceIJOrientation(&i, &j, nullptr);
+
+ // Determine the i- and j-offsets to the closest neighboring cell in each
+ // direction. This involves looking at the next bit of "i" and "j" to
+ // determine which quadrant of this->parent(level) this cell lies in.
+ int halfsize = GetSizeIJ(level + 1);
+ int size = halfsize << 1;
+ bool isame, jsame;
+ int ioffset, joffset;
+ if (i & halfsize) {
+ ioffset = size;
+ isame = (i + size) < kMaxSize;
+ } else {
+ ioffset = -size;
+ isame = (i - size) >= 0;
+ }
+ if (j & halfsize) {
+ joffset = size;
+ jsame = (j + size) < kMaxSize;
+ } else {
+ joffset = -size;
+ jsame = (j - size) >= 0;
+ }
+
+ output->push_back(parent(level));
+ output->push_back(FromFaceIJSame(face, i + ioffset, j, isame).parent(level));
+ output->push_back(FromFaceIJSame(face, i, j + joffset, jsame).parent(level));
+ // If i- and j- edge neighbors are *both* on a different face, then this
+ // vertex only has three neighbors (it is one of the 8 cube vertices).
+ if (isame || jsame) {
+ output->push_back(FromFaceIJSame(face, i + ioffset, j + joffset,
+ isame && jsame).parent(level));
+ }
+}
+
+void S2CellId::AppendAllNeighbors(int nbr_level,
+ vector<S2CellId>* output) const {
+ S2_DCHECK_GE(nbr_level, level());
+ int i, j;
+ int face = ToFaceIJOrientation(&i, &j, nullptr);
+
+ // Find the coordinates of the lower left-hand leaf cell. We need to
+ // normalize (i,j) to a known position within the cell because nbr_level
+ // may be larger than this cell's level.
+ int size = GetSizeIJ();
+ i &= -size;
+ j &= -size;
+
+ int nbr_size = GetSizeIJ(nbr_level);
+ S2_DCHECK_LE(nbr_size, size);
+
+ // We compute the top-bottom, left-right, and diagonal neighbors in one
+ // pass. The loop test is at the end of the loop to avoid 32-bit overflow.
+ for (int k = -nbr_size; ; k += nbr_size) {
+ bool same_face;
+ if (k < 0) {
+ same_face = (j + k >= 0);
+ } else if (k >= size) {
+ same_face = (j + k < kMaxSize);
+ } else {
+ same_face = true;
+ // Top and bottom neighbors.
+ output->push_back(FromFaceIJSame(face, i + k, j - nbr_size,
+ j - size >= 0).parent(nbr_level));
+ output->push_back(FromFaceIJSame(face, i + k, j + size,
+ j + size < kMaxSize).parent(nbr_level));
+ }
+ // Left, right, and diagonal neighbors.
+ output->push_back(FromFaceIJSame(face, i - nbr_size, j + k,
+ same_face && i - size >= 0)
+ .parent(nbr_level));
+ output->push_back(FromFaceIJSame(face, i + size, j + k,
+ same_face && i + size < kMaxSize)
+ .parent(nbr_level));
+ if (k >= size) break;
+ }
+}
+
+string S2CellId::ToString() const {
+ if (!is_valid()) {
+ return StrCat("Invalid: ", absl::Hex(id(), absl::kZeroPad16));
+ }
+ string out = StrCat(face(), "/");
+ for (int current_level = 1; current_level <= level(); ++current_level) {
+ // Avoid dependencies of SimpleItoA, and slowness of StrAppend &
+ // std::to_string.
+ out += "0123"[child_position(current_level)];
+ }
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& os, S2CellId id) {
+ return os << id.ToString();
+}
+
+S2CellId S2CellId::FromDebugString(absl::string_view str) {
+ // This function is reasonably efficient, but is only intended for use in
+ // tests.
+ int level = static_cast<int>(str.size() - 2);
+ if (level < 0 || level > S2CellId::kMaxLevel) return S2CellId::None();
+ int face = str[0] - '0';
+ if (face < 0 || face > 5 || str[1] != '/') return S2CellId::None();
+ S2CellId id = S2CellId::FromFace(face);
+ for (int i = 2; i < str.size(); ++i) {
+ int child_pos = str[i] - '0';
+ if (child_pos < 0 || child_pos > 3) return S2CellId::None();
+ id = id.child(child_pos);
+ }
+ return id;
+}
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2cell_index.h"
+
+using std::vector;
+
+using Label = S2CellIndex::Label;
+
+void S2CellIndex::RangeIterator::Seek(S2CellId target) {
+ S2_DCHECK(target.is_leaf());
+ it_ = std::upper_bound(range_nodes_->begin(), range_nodes_->end(),
+ target) - 1;
+}
+
+void S2CellIndex::ContentsIterator::StartUnion(const RangeIterator& range) {
+ if (range.start_id() < prev_start_id_) {
+ node_cutoff_ = -1; // Can't automatically eliminate duplicates.
+ }
+ prev_start_id_ = range.start_id();
+
+ // TODO(ericv): Since RangeNode only uses 12 of its 16 bytes, we could add a
+ // "label" field without using any extra space. Then we could store a leaf
+ // node of cell_tree_ directly in each RangeNode, where the cell_id is
+ // implicitly defined as the one that covers the current leaf cell range.
+ // This would save quite a bit of space; e.g. if the given cells are
+ // non-overlapping, then cell_tree_ would be empty (since every node is a
+ // leaf node and could therefore be stored directly in a RangeNode). It
+ // would also be faster because cell_tree_ would rarely be accessed.
+ int contents = range.it_->contents;
+ if (contents <= node_cutoff_) {
+ set_done();
+ } else {
+ node_ = (*cell_tree_)[contents];
+ }
+
+ // When visiting ancestors, we can stop as soon as the node index is smaller
+ // than any previously visited node index. Because indexes are assigned
+ // using a preorder traversal, such nodes are guaranteed to have already
+ // been reported.
+ next_node_cutoff_ = contents;
+}
+
+S2CellIndex::S2CellIndex() {
+}
+
+void S2CellIndex::Add(const S2CellUnion& cell_ids, Label label) {
+ for (S2CellId cell_id : cell_ids) {
+ Add(cell_id, label);
+ }
+}
+
+void S2CellIndex::Build() {
+ // To build the cell tree and leaf cell ranges, we maintain a stack of
+ // (cell_id, label) pairs that contain the current leaf cell. This class
+ // represents an instruction to push or pop a (cell_id, label) pair.
+ //
+ // If label >= 0, the (cell_id, label) pair is pushed on the stack.
+ // If cell_id == S2CellId::Sentinel(), a pair is popped from the stack.
+ // Otherwise the stack is unchanged but a RangeNode is still emitted.
+ struct Delta {
+ S2CellId start_id, cell_id;
+ Label label;
+
+ Delta(S2CellId _start_id, S2CellId _cell_id, Label _label)
+ : start_id(_start_id), cell_id(_cell_id), label(_label) {}
+
+ // Deltas are sorted first by start_id, then in reverse order by cell_id,
+ // and then by label. This is necessary to ensure that (1) larger cells
+ // are pushed on the stack before smaller cells, and (2) cells are popped
+ // off the stack before any new cells are added.
+ bool operator<(const Delta& y) const {
+ if (start_id < y.start_id) return true;
+ if (y.start_id < start_id) return false;
+ if (y.cell_id < cell_id) return true;
+ if (cell_id < y.cell_id) return false;
+ return label < y.label;
+ }
+ };
+
+ vector<Delta> deltas;
+ deltas.reserve(2 * cell_tree_.size() + 2);
+ // Create two deltas for each (cell_id, label) pair: one to add the pair to
+ // the stack (at the start of its leaf cell range), and one to remove it from
+ // the stack (at the end of its leaf cell range).
+ for (const CellNode& node : cell_tree_) {
+ deltas.push_back(Delta(node.cell_id.range_min(), node.cell_id, node.label));
+ deltas.push_back(Delta(node.cell_id.range_max().next(),
+ S2CellId::Sentinel(), -1));
+ }
+ // We also create two special deltas to ensure that a RangeNode is emitted at
+ // the beginning and end of the S2CellId range.
+ deltas.push_back(
+ Delta(S2CellId::Begin(S2CellId::kMaxLevel), S2CellId::None(), -1));
+ deltas.push_back(
+ Delta(S2CellId::End(S2CellId::kMaxLevel), S2CellId::None(), -1));
+ std::sort(deltas.begin(), deltas.end());
+
+ // Now walk through the deltas to build the leaf cell ranges and cell tree
+ // (which is essentially a permanent form of the "stack" described above).
+ cell_tree_.clear();
+ range_nodes_.reserve(deltas.size());
+ int contents = -1;
+ for (int i = 0; i < deltas.size(); ) {
+ S2CellId start_id = deltas[i].start_id;
+ // Process all the deltas associated with the current start_id.
+ for (; i < deltas.size() && deltas[i].start_id == start_id; ++i) {
+ if (deltas[i].label >= 0) {
+ cell_tree_.push_back({deltas[i].cell_id, deltas[i].label, contents});
+ contents = cell_tree_.size() - 1;
+ } else if (deltas[i].cell_id == S2CellId::Sentinel()) {
+ contents = cell_tree_[contents].parent;
+ }
+ }
+ range_nodes_.push_back({start_id, contents});
+ }
+}
+
+vector<Label> S2CellIndex::GetIntersectingLabels(const S2CellUnion& target)
+ const {
+ vector<Label> labels;
+ GetIntersectingLabels(target, &labels);
+ return labels;
+}
+
+void S2CellIndex::GetIntersectingLabels(const S2CellUnion& target,
+ std::vector<Label>* labels) const {
+ labels->clear();
+ VisitIntersectingCells(target, [labels](S2CellId cell_id, Label label) {
+ labels->push_back(label);
+ return true;
+ });
+ std::sort(labels->begin(), labels->end());
+ labels->erase(std::unique(labels->begin(), labels->end()), labels->end());
+}
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2cell_union.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+#include "s2/s1angle.h"
+#include "s2/s2cap.h"
+#include "s2/s2cell.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2latlng_rect.h"
+#include "s2/s2metrics.h"
+#include "s2/util/coding/coder.h"
+
+using std::is_sorted;
+using std::max;
+using std::min;
+using std::vector;
+
+DEFINE_int32(s2cell_union_decode_max_num_cells, 1000000,
+ "The maximum number of cells allowed by S2CellUnion::Decode");
+
+static const unsigned char kCurrentLosslessEncodingVersionNumber = 1;
+
+vector<S2CellId> S2CellUnion::ToS2CellIds(const vector<uint64>& ids) {
+ vector<S2CellId> cell_ids;
+ cell_ids.reserve(ids.size());
+ for (auto id : ids) cell_ids.push_back(S2CellId(id));
+ return cell_ids;
+}
+
+S2CellUnion::S2CellUnion(const vector<uint64>& cell_ids)
+ : cell_ids_(ToS2CellIds(cell_ids)) {
+ Normalize();
+}
+
+S2CellUnion S2CellUnion::WholeSphere() {
+ return S2CellUnion({S2CellId::FromFace(0), S2CellId::FromFace(1),
+ S2CellId::FromFace(2), S2CellId::FromFace(3),
+ S2CellId::FromFace(4), S2CellId::FromFace(5)});
+}
+
+S2CellUnion S2CellUnion::FromMinMax(S2CellId min_id, S2CellId max_id) {
+ S2CellUnion result;
+ result.InitFromMinMax(min_id, max_id);
+ return result;
+}
+
+S2CellUnion S2CellUnion::FromBeginEnd(S2CellId begin, S2CellId end) {
+ S2CellUnion result;
+ result.InitFromBeginEnd(begin, end);
+ return result;
+}
+
+void S2CellUnion::Init(const vector<uint64>& cell_ids) {
+ cell_ids_ = ToS2CellIds(cell_ids);
+ Normalize();
+}
+
+void S2CellUnion::InitFromMinMax(S2CellId min_id, S2CellId max_id) {
+ S2_DCHECK(max_id.is_valid());
+ InitFromBeginEnd(min_id, max_id.next());
+}
+
+void S2CellUnion::InitFromBeginEnd(S2CellId begin, S2CellId end) {
+ S2_DCHECK(begin.is_leaf());
+ S2_DCHECK(end.is_leaf());
+ S2_DCHECK_LE(begin, end);
+
+ // We repeatedly add the largest cell we can.
+ cell_ids_.clear();
+ for (S2CellId id = begin.maximum_tile(end);
+ id != end; id = id.next().maximum_tile(end)) {
+ cell_ids_.push_back(id);
+ }
+ // The output is already normalized.
+ S2_DCHECK(IsNormalized());
+}
+
+void S2CellUnion::Pack(int excess) {
+ if (cell_ids_.capacity() - cell_ids_.size() > excess) {
+ cell_ids_.shrink_to_fit();
+ }
+}
+
+S2CellUnion* S2CellUnion::Clone() const {
+ return new S2CellUnion(cell_ids_, VERBATIM);
+}
+
+// Returns true if the given four cells have a common parent.
+// REQUIRES: The four cells are distinct.
+inline static bool AreSiblings(S2CellId a, S2CellId b, S2CellId c, S2CellId d) {
+ // A necessary (but not sufficient) condition is that the XOR of the
+ // four cells must be zero. This is also very fast to test.
+ if ((a.id() ^ b.id() ^ c.id()) != d.id()) return false;
+
+ // Now we do a slightly more expensive but exact test. First, compute a
+ // mask that blocks out the two bits that encode the child position of
+ // "id" with respect to its parent, then check that the other three
+ // children all agree with "mask".
+ uint64 mask = d.lsb() << 1;
+ mask = ~(mask + (mask << 1));
+ uint64 id_masked = (d.id() & mask);
+ return ((a.id() & mask) == id_masked &&
+ (b.id() & mask) == id_masked &&
+ (c.id() & mask) == id_masked &&
+ !d.is_face());
+}
+
+bool S2CellUnion::IsValid() const {
+ if (num_cells() > 0 && !cell_id(0).is_valid()) return false;
+ for (int i = 1; i < num_cells(); ++i) {
+ if (!cell_id(i).is_valid()) return false;
+ if (cell_id(i - 1).range_max() >= cell_id(i).range_min()) return false;
+ }
+ return true;
+}
+
+bool S2CellUnion::IsNormalized() const {
+ if (num_cells() > 0 && !cell_id(0).is_valid()) return false;
+ for (int i = 1; i < num_cells(); ++i) {
+ if (!cell_id(i).is_valid()) return false;
+ if (cell_id(i - 1).range_max() >= cell_id(i).range_min()) return false;
+ if (i >= 3 && AreSiblings(cell_id(i - 3), cell_id(i - 2),
+ cell_id(i - 1), cell_id(i))) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool S2CellUnion::Normalize() {
+ return Normalize(&cell_ids_);
+}
+
+/*static*/ bool S2CellUnion::Normalize(vector<S2CellId>* ids) {
+ // Optimize the representation by discarding cells contained by other cells,
+ // and looking for cases where all subcells of a parent cell are present.
+
+ std::sort(ids->begin(), ids->end());
+ int out = 0;
+ for (S2CellId id : *ids) {
+ // Check whether this cell is contained by the previous cell.
+ if (out > 0 && (*ids)[out-1].contains(id)) continue;
+
+ // Discard any previous cells contained by this cell.
+ while (out > 0 && id.contains((*ids)[out-1])) --out;
+
+ // Check whether the last 3 elements plus "id" can be collapsed into a
+ // single parent cell.
+ while (out >= 3 && AreSiblings((*ids)[out - 3], (*ids)[out - 2],
+ (*ids)[out - 1], id)) {
+ // Replace four children by their parent cell.
+ id = id.parent();
+ out -= 3;
+ }
+ (*ids)[out++] = id;
+ }
+ if (ids->size() == out) return false;
+ ids->resize(out);
+ return true;
+}
+
+void S2CellUnion::Denormalize(int min_level, int level_mod,
+ vector<S2CellId>* out) const {
+ Denormalize(cell_ids_, min_level, level_mod, out);
+}
+
+void S2CellUnion::Denormalize(const vector<S2CellId>& in,
+ int min_level, int level_mod,
+ vector<S2CellId>* out) {
+ S2_DCHECK_GE(min_level, 0);
+ S2_DCHECK_LE(min_level, S2CellId::kMaxLevel);
+ S2_DCHECK_GE(level_mod, 1);
+ S2_DCHECK_LE(level_mod, 3);
+ S2_DCHECK_NE(out, &in);
+
+ out->clear();
+ out->reserve(in.size());
+ for (S2CellId id : in) {
+ int level = id.level();
+ int new_level = max(min_level, level);
+ if (level_mod > 1) {
+ // Round up so that (new_level - min_level) is a multiple of level_mod.
+ // (Note that S2CellId::kMaxLevel is a multiple of 1, 2, and 3.)
+ new_level += (S2CellId::kMaxLevel - (new_level - min_level)) % level_mod;
+ new_level = min(S2CellId::kMaxLevel, new_level);
+ }
+ if (new_level == level) {
+ out->push_back(id);
+ } else {
+ S2CellId end = id.child_end(new_level);
+ for (id = id.child_begin(new_level); id != end; id = id.next()) {
+ out->push_back(id);
+ }
+ }
+ }
+}
+
+S2Cap S2CellUnion::GetCapBound() const {
+ // Compute the approximate centroid of the region. This won't produce the
+ // bounding cap of minimal area, but it should be close enough.
+ if (cell_ids_.empty()) return S2Cap::Empty();
+ S2Point centroid(0, 0, 0);
+ for (S2CellId id : *this) {
+ double area = S2Cell::AverageArea(id.level());
+ centroid += area * id.ToPoint();
+ }
+ if (centroid == S2Point(0, 0, 0)) {
+ centroid = S2Point(1, 0, 0);
+ } else {
+ centroid = centroid.Normalize();
+ }
+
+ // Use the centroid as the cap axis, and expand the cap angle so that it
+ // contains the bounding caps of all the individual cells. Note that it is
+ // *not* sufficient to just bound all the cell vertices because the bounding
+ // cap may be concave (i.e. cover more than one hemisphere).
+ S2Cap cap = S2Cap::FromPoint(centroid);
+ for (S2CellId id : *this) {
+ cap.AddCap(S2Cell(id).GetCapBound());
+ }
+ return cap;
+}
+
+S2LatLngRect S2CellUnion::GetRectBound() const {
+ S2LatLngRect bound = S2LatLngRect::Empty();
+ for (S2CellId id : *this) {
+ bound = bound.Union(S2Cell(id).GetRectBound());
+ }
+ return bound;
+}
+
+bool S2CellUnion::Contains(S2CellId id) const {
+ // This is an exact test. Each cell occupies a linear span of the S2
+ // space-filling curve, and the cell id is simply the position at the center
+ // of this span. The cell union ids are sorted in increasing order along
+ // the space-filling curve. So we simply find the pair of cell ids that
+ // surround the given cell id (using binary search). There is containment
+ // if and only if one of these two cell ids contains this cell.
+
+ vector<S2CellId>::const_iterator i =
+ std::lower_bound(cell_ids_.begin(), cell_ids_.end(), id);
+ if (i != cell_ids_.end() && i->range_min() <= id) return true;
+ return i != cell_ids_.begin() && (--i)->range_max() >= id;
+}
+
+bool S2CellUnion::Intersects(S2CellId id) const {
+ // This is an exact test; see the comments for Contains() above.
+
+ vector<S2CellId>::const_iterator i =
+ std::lower_bound(cell_ids_.begin(), cell_ids_.end(), id);
+ if (i != cell_ids_.end() && i->range_min() <= id.range_max()) return true;
+ return i != cell_ids_.begin() && (--i)->range_max() >= id.range_min();
+}
+
+bool S2CellUnion::Contains(const S2CellUnion& y) const {
+ // TODO(ericv): A divide-and-conquer or alternating-skip-search
+ // approach may be sigificantly faster in both the average and worst case.
+
+ for (S2CellId y_id : y) {
+ if (!Contains(y_id)) return false;
+ }
+ return true;
+}
+
+bool S2CellUnion::Intersects(const S2CellUnion& y) const {
+ // TODO(ericv): A divide-and-conquer or alternating-skip-search
+ // approach may be sigificantly faster in both the average and worst case.
+
+ for (S2CellId y_id : y) {
+ if (Intersects(y_id)) return true;
+ }
+ return false;
+}
+
+S2CellUnion S2CellUnion::Union(const S2CellUnion& y) const {
+ vector<S2CellId> cell_ids;
+ cell_ids.reserve(num_cells() + y.num_cells());
+ cell_ids = cell_ids_;
+ cell_ids.insert(cell_ids.end(), y.cell_ids_.begin(), y.cell_ids_.end());
+ return S2CellUnion(std::move(cell_ids));
+}
+
+S2CellUnion S2CellUnion::Intersection(S2CellId id) const {
+ S2CellUnion result;
+ if (Contains(id)) {
+ result.cell_ids_.push_back(id);
+ } else {
+ vector<S2CellId>::const_iterator i = std::lower_bound(
+ cell_ids_.begin(), cell_ids_.end(), id.range_min());
+ S2CellId id_max = id.range_max();
+ while (i != cell_ids_.end() && *i <= id_max)
+ result.cell_ids_.push_back(*i++);
+ }
+ S2_DCHECK(result.IsNormalized() || !IsNormalized());
+ return result;
+}
+
+S2CellUnion S2CellUnion::Intersection(const S2CellUnion& y) const {
+ S2CellUnion result;
+ GetIntersection(cell_ids_, y.cell_ids_, &result.cell_ids_);
+ // The output is normalized as long as at least one input is normalized.
+ S2_DCHECK(result.IsNormalized() || (!IsNormalized() && !y.IsNormalized()));
+ return result;
+}
+
+/*static*/ void S2CellUnion::GetIntersection(const vector<S2CellId>& x,
+ const vector<S2CellId>& y,
+ vector<S2CellId>* out) {
+ S2_DCHECK_NE(out, &x);
+ S2_DCHECK_NE(out, &y);
+ S2_DCHECK(is_sorted(x.begin(), x.end()));
+ S2_DCHECK(is_sorted(y.begin(), y.end()));
+
+ // This is a fairly efficient calculation that uses binary search to skip
+ // over sections of both input vectors. It takes logarithmic time if all the
+ // cells of "x" come before or after all the cells of "y" in S2CellId order.
+
+ out->clear();
+ vector<S2CellId>::const_iterator i = x.begin();
+ vector<S2CellId>::const_iterator j = y.begin();
+ while (i != x.end() && j != y.end()) {
+ S2CellId imin = i->range_min();
+ S2CellId jmin = j->range_min();
+ if (imin > jmin) {
+ // Either j->contains(*i) or the two cells are disjoint.
+ if (*i <= j->range_max()) {
+ out->push_back(*i++);
+ } else {
+ // Advance "j" to the first cell possibly contained by *i.
+ j = std::lower_bound(j + 1, y.end(), imin);
+ // The previous cell *(j-1) may now contain *i.
+ if (*i <= (j - 1)->range_max()) --j;
+ }
+ } else if (jmin > imin) {
+ // Identical to the code above with "i" and "j" reversed.
+ if (*j <= i->range_max()) {
+ out->push_back(*j++);
+ } else {
+ i = std::lower_bound(i + 1, x.end(), jmin);
+ if (*j <= (i - 1)->range_max()) --i;
+ }
+ } else {
+ // "i" and "j" have the same range_min(), so one contains the other.
+ if (*i < *j)
+ out->push_back(*i++);
+ else
+ out->push_back(*j++);
+ }
+ }
+ // The output is generated in sorted order.
+ S2_DCHECK(is_sorted(out->begin(), out->end()));
+}
+
+static void GetDifferenceInternal(S2CellId cell,
+ const S2CellUnion& y,
+ vector<S2CellId>* cell_ids) {
+ // Add the difference between cell and y to cell_ids.
+ // If they intersect but the difference is non-empty, divide and conquer.
+ if (!y.Intersects(cell)) {
+ cell_ids->push_back(cell);
+ } else if (!y.Contains(cell)) {
+ S2CellId child = cell.child_begin();
+ for (int i = 0; ; ++i) {
+ GetDifferenceInternal(child, y, cell_ids);
+ if (i == 3) break; // Avoid unnecessary next() computation.
+ child = child.next();
+ }
+ }
+}
+
+S2CellUnion S2CellUnion::Difference(const S2CellUnion& y) const {
+ // TODO(ericv): this is approximately O(N*log(N)), but could probably
+ // use similar techniques as GetIntersection() to be more efficient.
+
+ S2CellUnion result;
+ for (S2CellId id : *this) {
+ GetDifferenceInternal(id, y, &result.cell_ids_);
+ }
+ // The output is normalized as long as the first argument is normalized.
+ S2_DCHECK(result.IsNormalized() || !IsNormalized());
+ return result;
+}
+
+void S2CellUnion::Expand(int expand_level) {
+ vector<S2CellId> output;
+ uint64 level_lsb = S2CellId::lsb_for_level(expand_level);
+ for (int i = num_cells(); --i >= 0; ) {
+ S2CellId id = cell_id(i);
+ if (id.lsb() < level_lsb) {
+ id = id.parent(expand_level);
+ // Optimization: skip over any cells contained by this one. This is
+ // especially important when very small regions are being expanded.
+ while (i > 0 && id.contains(cell_id(i - 1))) --i;
+ }
+ output.push_back(id);
+ id.AppendAllNeighbors(expand_level, &output);
+ }
+ Init(std::move(output));
+}
+
+void S2CellUnion::Expand(S1Angle min_radius, int max_level_diff) {
+ int min_level = S2CellId::kMaxLevel;
+ for (S2CellId id : *this) {
+ min_level = min(min_level, id.level());
+ }
+ // Find the maximum level such that all cells are at least "min_radius" wide.
+ int radius_level = S2::kMinWidth.GetLevelForMinValue(min_radius.radians());
+ if (radius_level == 0 && min_radius.radians() > S2::kMinWidth.GetValue(0)) {
+ // The requested expansion is greater than the width of a face cell.
+ // The easiest way to handle this is to expand twice.
+ Expand(0);
+ }
+ Expand(min(min_level + max_level_diff, radius_level));
+}
+
+uint64 S2CellUnion::LeafCellsCovered() const {
+ uint64 num_leaves = 0;
+ for (S2CellId id : *this) {
+ const int inverted_level = S2CellId::kMaxLevel - id.level();
+ num_leaves += (1ULL << (inverted_level << 1));
+ }
+ return num_leaves;
+}
+
+double S2CellUnion::AverageBasedArea() const {
+ return S2Cell::AverageArea(S2CellId::kMaxLevel) * LeafCellsCovered();
+}
+
+double S2CellUnion::ApproxArea() const {
+ double area = 0;
+ for (S2CellId id : *this) {
+ area += S2Cell(id).ApproxArea();
+ }
+ return area;
+}
+
+double S2CellUnion::ExactArea() const {
+ double area = 0;
+ for (S2CellId id : *this) {
+ area += S2Cell(id).ExactArea();
+ }
+ return area;
+}
+
+bool operator==(const S2CellUnion& x, const S2CellUnion& y) {
+ return x.cell_ids() == y.cell_ids();
+}
+
+bool operator!=(const S2CellUnion& x, const S2CellUnion& y) {
+ return x.cell_ids() != y.cell_ids();
+}
+
+bool S2CellUnion::Contains(const S2Cell& cell) const {
+ return Contains(cell.id());
+}
+
+bool S2CellUnion::MayIntersect(const S2Cell& cell) const {
+ return Intersects(cell.id());
+}
+
+void S2CellUnion::Encode(Encoder* const encoder) const {
+ // Unsigned char for version number, and N+1 uint64's for N cell_ids
+ // (1 for vector length, N for the ids).
+ encoder->Ensure(sizeof(unsigned char) +
+ sizeof(uint64) * (1 + cell_ids_.size()));
+
+ encoder->put8(kCurrentLosslessEncodingVersionNumber);
+ encoder->put64(uint64{cell_ids_.size()});
+ for (const S2CellId& cell_id : cell_ids_) {
+ cell_id.Encode(encoder);
+ }
+}
+
+bool S2CellUnion::Decode(Decoder* const decoder) {
+ // Should contain at least version and vector length.
+ if (decoder->avail() < sizeof(unsigned char) + sizeof(uint64)) return false;
+ unsigned char version = decoder->get8();
+ if (version > kCurrentLosslessEncodingVersionNumber) return false;
+
+ uint64 num_cells = decoder->get64();
+ if (num_cells > FLAGS_s2cell_union_decode_max_num_cells) {
+ return false;
+ }
+
+ vector<S2CellId> temp_cell_ids(num_cells);
+ for (int i = 0; i < num_cells; ++i) {
+ if (!temp_cell_ids[i].Decode(decoder)) return false;
+ }
+ cell_ids_.swap(temp_cell_ids);
+ return true;
+}
+
+bool S2CellUnion::Contains(const S2Point& p) const {
+ return Contains(S2CellId(p));
+}
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2centroids.h"
+
+#include <cmath>
+
+#include "s2/s2pointutil.h"
+
+namespace S2 {
+
+S2Point PlanarCentroid(const S2Point& a, const S2Point& b, const S2Point& c) {
+ return (1./3) * (a + b + c);
+}
+
+S2Point TrueCentroid(const S2Point& a, const S2Point& b, const S2Point& c) {
+ S2_DCHECK(IsUnitLength(a));
+ S2_DCHECK(IsUnitLength(b));
+ S2_DCHECK(IsUnitLength(c));
+
+ // I couldn't find any references for computing the true centroid of a
+ // spherical triangle... I have a truly marvellous demonstration of this
+ // formula which this margin is too narrow to contain :)
+
+ // Use Angle() in order to get accurate results for small triangles.
+ double angle_a = b.Angle(c);
+ double angle_b = c.Angle(a);
+ double angle_c = a.Angle(b);
+ double ra = (angle_a == 0) ? 1 : (angle_a / std::sin(angle_a));
+ double rb = (angle_b == 0) ? 1 : (angle_b / std::sin(angle_b));
+ double rc = (angle_c == 0) ? 1 : (angle_c / std::sin(angle_c));
+
+ // Now compute a point M such that:
+ //
+ // [Ax Ay Az] [Mx] [ra]
+ // [Bx By Bz] [My] = 0.5 * det(A,B,C) * [rb]
+ // [Cx Cy Cz] [Mz] [rc]
+ //
+ // To improve the numerical stability we subtract the first row (A) from the
+ // other two rows; this reduces the cancellation error when A, B, and C are
+ // very close together. Then we solve it using Cramer's rule.
+ //
+ // The result is the true centroid of the triangle multiplied by the
+ // triangle's area.
+ //
+ // TODO(ericv): This code still isn't as numerically stable as it could be.
+ // The biggest potential improvement is to compute B-A and C-A more
+ // accurately so that (B-A)x(C-A) is always inside triangle ABC.
+ S2Point x(a.x(), b.x() - a.x(), c.x() - a.x());
+ S2Point y(a.y(), b.y() - a.y(), c.y() - a.y());
+ S2Point z(a.z(), b.z() - a.z(), c.z() - a.z());
+ S2Point r(ra, rb - ra, rc - ra);
+ return 0.5 * S2Point(y.CrossProd(z).DotProd(r),
+ z.CrossProd(x).DotProd(r),
+ x.CrossProd(y).DotProd(r));
+}
+
+S2Point TrueCentroid(const S2Point& a, const S2Point& b) {
+ // The centroid (multiplied by length) is a vector toward the midpoint
+ // of the edge, whose length is twice the sine of half the angle between
+ // the two vertices. Defining theta to be this angle, we have:
+ S2Point vdiff = a - b; // Length == 2*sin(theta)
+ S2Point vsum = a + b; // Length == 2*cos(theta)
+ double sin2 = vdiff.Norm2();
+ double cos2 = vsum.Norm2();
+ if (cos2 == 0) return S2Point(); // Ignore antipodal edges.
+ return sqrt(sin2 / cos2) * vsum; // Length == 2*sin(theta)
+}
+
+} // namespace S2
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2closest_cell_query.h"
+
+#include <memory>
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/s1angle.h"
+#include "s2/s2cap.h"
+#include "s2/s2cell.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2cell_union.h"
+#include "s2/s2edge_distances.h"
+#include "s2/s2region_coverer.h"
+#include "s2/s2shape_index_region.h"
+
+void S2ClosestCellQuery::Options::set_conservative_max_distance(
+ S1ChordAngle max_distance) {
+ set_max_distance(Distance(max_distance.PlusError(
+ S2::GetUpdateMinDistanceMaxError(max_distance)).Successor()));
+}
+
+void S2ClosestCellQuery::Options::set_conservative_max_distance(
+ S1Angle max_distance) {
+ set_conservative_max_distance(S1ChordAngle(max_distance));
+}
+
+// The thresholds for using the brute force algorithm are generally tuned to
+// optimize IsDistanceLess (which compares the distance against a threshold)
+// rather than FindClosest (which actually computes the minimum distance).
+// This is because the former operation is (1) more common, (2) inherently
+// faster, and (3) closely related to finding all cells within a given
+// distance, which is also very common.
+
+int S2ClosestCellQuery::PointTarget::max_brute_force_index_size() const {
+ // Break-even points: Point cloud Cap coverings
+ // BM_FindClosest 18 16
+ // BM_IsDistanceLess 8 9
+ return 9;
+}
+
+int S2ClosestCellQuery::EdgeTarget::max_brute_force_index_size() const {
+ // Break-even points: Point cloud Cap coverings
+ // BM_FindClosestToLongEdge 14 16
+ // BM_IsDistanceLessToLongEdge 5 5
+ return 5;
+}
+
+int S2ClosestCellQuery::CellTarget::max_brute_force_index_size() const {
+ // Break-even points: Point cloud Cap coverings
+ // BM_FindClosestToSmallCell 12 13
+ // BM_IsDistanceLessToSmallCell 6 6
+ //
+ // Note that the primary use of CellTarget is to implement CellUnionTarget,
+ // and therefore it is very important to optimize for the case where a
+ // distance limit has been specified.
+ return 6;
+}
+
+int S2ClosestCellQuery::CellUnionTarget::max_brute_force_index_size() const {
+ // Break-even points: Point cloud Cap coverings
+ // BM_FindClosestToSmallCoarseCellUnion 12 10
+ // BM_IsDistanceLessToSmallCoarseCellUnion 7 6
+ return 8;
+}
+
+int S2ClosestCellQuery::ShapeIndexTarget::max_brute_force_index_size() const {
+ // Break-even points: Point cloud Cap coverings
+ // BM_FindClosestToSmallCoarseShapeIndex 10 8
+ // BM_IsDistanceLessToSmallCoarseShapeIndex 7 6
+ return 7;
+}
+
+S2ClosestCellQuery::S2ClosestCellQuery() {
+ // Prevent inline constructor bloat by defining here.
+}
+
+S2ClosestCellQuery::~S2ClosestCellQuery() {
+ // Prevent inline destructor bloat by defining here.
+}
+
+bool S2ClosestCellQuery::IsDistanceLess(Target* target, S1ChordAngle limit) {
+ static_assert(sizeof(Options) <= 32, "Consider not copying Options here");
+ Options tmp_options = options_;
+ tmp_options.set_max_results(1);
+ tmp_options.set_max_distance(limit);
+ tmp_options.set_max_error(S1ChordAngle::Straight());
+ return !base_.FindClosestCell(target, tmp_options).is_empty();
+}
+
+bool S2ClosestCellQuery::IsDistanceLessOrEqual(Target* target,
+ S1ChordAngle limit) {
+ static_assert(sizeof(Options) <= 32, "Consider not copying Options here");
+ Options tmp_options = options_;
+ tmp_options.set_max_results(1);
+ tmp_options.set_inclusive_max_distance(limit);
+ tmp_options.set_max_error(S1ChordAngle::Straight());
+ return !base_.FindClosestCell(target, tmp_options).is_empty();
+}
+
+bool S2ClosestCellQuery::IsConservativeDistanceLessOrEqual(
+ Target* target, S1ChordAngle limit) {
+ static_assert(sizeof(Options) <= 32, "Consider not copying Options here");
+ Options tmp_options = options_;
+ tmp_options.set_max_results(1);
+ tmp_options.set_conservative_max_distance(limit);
+ tmp_options.set_max_error(S1ChordAngle::Straight());
+ return !base_.FindClosestCell(target, tmp_options).is_empty();
+}
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2closest_edge_query.h"
+
+#include <memory>
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/s1angle.h"
+#include "s2/s2cap.h"
+#include "s2/s2cell.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2cell_union.h"
+#include "s2/s2edge_distances.h"
+#include "s2/s2region_coverer.h"
+#include "s2/s2shape_index_region.h"
+
+void S2ClosestEdgeQuery::Options::set_conservative_max_distance(
+ S1ChordAngle max_distance) {
+ set_max_distance(Distance(max_distance.PlusError(
+ S2::GetUpdateMinDistanceMaxError(max_distance)).Successor()));
+}
+
+void S2ClosestEdgeQuery::Options::set_conservative_max_distance(
+ S1Angle max_distance) {
+ set_conservative_max_distance(S1ChordAngle(max_distance));
+}
+
+int S2ClosestEdgeQuery::PointTarget::max_brute_force_index_size() const {
+ // Using BM_FindClosest (which finds the single closest edge), the
+ // break-even points are approximately 80, 100, and 250 edges for point
+ // cloud, fractal, and regular loop geometry respectively.
+ return 120;
+}
+
+int S2ClosestEdgeQuery::EdgeTarget::max_brute_force_index_size() const {
+ // Using BM_FindClosestToEdge (which finds the single closest edge), the
+ // break-even points are approximately 40, 50, and 100 edges for point
+ // cloud, fractal, and regular loop geometry respectively.
+ return 60;
+}
+
+int S2ClosestEdgeQuery::CellTarget::max_brute_force_index_size() const {
+ // Using BM_FindClosestToCell (which finds the single closest edge), the
+ // break-even points are approximately 20, 25, and 40 edges for point cloud,
+ // fractal, and regular loop geometry respectively.
+ return 30;
+}
+
+int S2ClosestEdgeQuery::ShapeIndexTarget::max_brute_force_index_size() const {
+ // For BM_FindClosestToSameSizeAbuttingIndex (which uses two nearby indexes
+ // with similar edge counts), the break-even points are approximately 20,
+ // 30, and 40 edges for point cloud, fractal, and regular loop geometry
+ // respectively.
+ return 25;
+}
+
+S2ClosestEdgeQuery::S2ClosestEdgeQuery() {
+ // Prevent inline constructor bloat by defining here.
+}
+
+S2ClosestEdgeQuery::~S2ClosestEdgeQuery() {
+ // Prevent inline destructor bloat by defining here.
+}
+
+bool S2ClosestEdgeQuery::IsDistanceLess(Target* target, S1ChordAngle limit) {
+ static_assert(sizeof(Options) <= 32, "Consider not copying Options here");
+ Options tmp_options = options_;
+ tmp_options.set_max_results(1);
+ tmp_options.set_max_distance(limit);
+ tmp_options.set_max_error(S1ChordAngle::Straight());
+ return !base_.FindClosestEdge(target, tmp_options).is_empty();
+}
+
+bool S2ClosestEdgeQuery::IsDistanceLessOrEqual(Target* target,
+ S1ChordAngle limit) {
+ static_assert(sizeof(Options) <= 32, "Consider not copying Options here");
+ Options tmp_options = options_;
+ tmp_options.set_max_results(1);
+ tmp_options.set_inclusive_max_distance(limit);
+ tmp_options.set_max_error(S1ChordAngle::Straight());
+ return !base_.FindClosestEdge(target, tmp_options).is_empty();
+}
+
+bool S2ClosestEdgeQuery::IsConservativeDistanceLessOrEqual(
+ Target* target, S1ChordAngle limit) {
+ static_assert(sizeof(Options) <= 32, "Consider not copying Options here");
+ Options tmp_options = options_;
+ tmp_options.set_max_results(1);
+ tmp_options.set_conservative_max_distance(limit);
+ tmp_options.set_max_error(S1ChordAngle::Straight());
+ return !base_.FindClosestEdge(target, tmp_options).is_empty();
+}
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2closest_point_query.h"
+
+void S2ClosestPointQueryOptions::set_conservative_max_distance(
+ S1ChordAngle max_distance) {
+ set_max_distance(Distance(max_distance.PlusError(
+ S2::GetUpdateMinDistanceMaxError(max_distance)).Successor()));
+}
+
+void S2ClosestPointQueryOptions::set_conservative_max_distance(
+ S1Angle max_distance) {
+ set_conservative_max_distance(S1ChordAngle(max_distance));
+}
+
+int S2ClosestPointQueryPointTarget::max_brute_force_index_size() const {
+ // Using BM_FindClosest (which finds the single closest point), the
+ // break-even points are approximately X, Y, and Z points for grid,
+ // fractal, and regular loop geometry respectively.
+ //
+ // TODO(ericv): Adjust using benchmarks.
+ return 150;
+}
+
+int S2ClosestPointQueryEdgeTarget::max_brute_force_index_size() const {
+ // Using BM_FindClosestToEdge (which finds the single closest point), the
+ // break-even points are approximately X, Y, and Z points for grid,
+ // fractal, and regular loop geometry respectively.
+ //
+ // TODO(ericv): Adjust using benchmarks.
+ return 100;
+}
+
+int S2ClosestPointQueryCellTarget::max_brute_force_index_size() const {
+ // Using BM_FindClosestToCell (which finds the single closest point), the
+ // break-even points are approximately X, Y, and Z points for grid,
+ // fractal, and regular loop geometry respectively.
+ //
+ // TODO(ericv): Adjust using benchmarks.
+ return 50;
+}
+
+int S2ClosestPointQueryShapeIndexTarget::max_brute_force_index_size() const {
+ // For BM_FindClosestToSameSizeAbuttingIndex (which uses a nearby
+ // S2ShapeIndex target of similar complexity), the break-even points are
+ // approximately X, Y, and Z points for grid, fractal, and regular loop
+ // geometry respectively.
+ //
+ // TODO(ericv): Adjust using benchmarks.
+ return 30;
+}
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2contains_vertex_query.h"
+
+#include <cmath>
+#include <utility>
+#include "s2/s2pointutil.h"
+#include "s2/s2predicates.h"
+
+using std::abs;
+
+int S2ContainsVertexQuery::ContainsSign() {
+ // Find the unmatched edge that is immediately clockwise from S2::Ortho(P).
+ S2Point reference_dir = S2::Ortho(target_);
+ std::pair<S2Point, int> best(reference_dir, 0);
+ for (const auto& e : edge_map_) {
+ S2_DCHECK_LE(abs(e.second), 1);
+ if (e.second == 0) continue; // This is a "matched" edge.
+ if (s2pred::OrderedCCW(reference_dir, best.first, e.first, target_)) {
+ best = e;
+ }
+ }
+ return best.second;
+}
--- /dev/null
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// This implement Andrew's monotone chain algorithm, which is a variant of the
+// Graham scan (see https://en.wikipedia.org/wiki/Graham_scan). The time
+// complexity is O(n log n), and the space required is O(n). In fact only the
+// call to "sort" takes O(n log n) time; the rest of the algorithm is linear.
+//
+// Demonstration of the algorithm and code:
+// en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain
+
+#include "s2/s2convex_hull_query.h"
+
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/s2pointutil.h"
+#include "s2/s2predicates.h"
+
+using absl::make_unique;
+using std::unique_ptr;
+using std::vector;
+
+S2ConvexHullQuery::S2ConvexHullQuery()
+ : bound_(S2LatLngRect::Empty()), points_() {
+}
+
+void S2ConvexHullQuery::AddPoint(const S2Point& point) {
+ bound_.AddPoint(point);
+ points_.push_back(point);
+}
+
+void S2ConvexHullQuery::AddPolyline(const S2Polyline& polyline) {
+ bound_ = bound_.Union(polyline.GetRectBound());
+ for (int i = 0; i < polyline.num_vertices(); ++i) {
+ points_.push_back(polyline.vertex(i));
+ }
+}
+
+void S2ConvexHullQuery::AddLoop(const S2Loop& loop) {
+ bound_ = bound_.Union(loop.GetRectBound());
+ if (loop.is_empty_or_full()) {
+ // The empty and full loops consist of a single fake "vertex" that should
+ // not be added to our point collection.
+ return;
+ }
+ for (int i = 0; i < loop.num_vertices(); ++i) {
+ points_.push_back(loop.vertex(i));
+ }
+}
+
+void S2ConvexHullQuery::AddPolygon(const S2Polygon& polygon) {
+ for (int i = 0; i < polygon.num_loops(); ++i) {
+ const S2Loop& loop = *polygon.loop(i);
+ // Only loops at depth 0 can contribute to the convex hull.
+ if (loop.depth() == 0) {
+ AddLoop(loop);
+ }
+ }
+}
+
+S2Cap S2ConvexHullQuery::GetCapBound() {
+ // We keep track of a rectangular bound rather than a spherical cap because
+ // it is easy to compute a tight bound for a union of rectangles, whereas it
+ // is quite difficult to compute a tight bound around a union of caps.
+ // Also, polygons and polylines implement GetCapBound() in terms of
+ // GetRectBound() for this same reason, so it is much better to keep track
+ // of a rectangular bound as we go along and convert it at the end.
+ //
+ // TODO(ericv): We could compute an optimal bound by implementing Welzl's
+ // algorithm. However we would still need to have special handling of loops
+ // and polygons, since if a loop spans more than 180 degrees in any
+ // direction (i.e., if it contains two antipodal points), then it is not
+ // enough just to bound its vertices. In this case the only convex bounding
+ // cap is S2Cap::Full(), and the only convex bounding loop is the full loop.
+ return bound_.GetCapBound();
+}
+
+// A comparator for sorting points in CCW around a central point "center".
+class OrderedCcwAround {
+ public:
+ explicit OrderedCcwAround(const S2Point& center) : center_(center) {}
+ bool operator()(const S2Point& x, const S2Point& y) const {
+ // If X and Y are equal, this will return false (as desired).
+ return s2pred::Sign(center_, x, y) > 0;
+ }
+ private:
+ S2Point center_;
+};
+
+unique_ptr<S2Loop> S2ConvexHullQuery::GetConvexHull() {
+ S2Cap cap = GetCapBound();
+ if (cap.height() >= 1) {
+ // The bounding cap is not convex. The current bounding cap
+ // implementation is not optimal, but nevertheless it is likely that the
+ // input geometry itself is not contained by any convex polygon. In any
+ // case, we need a convex bounding cap to proceed with the algorithm below
+ // (in order to construct a point "origin" that is definitely outside the
+ // convex hull).
+ return make_unique<S2Loop>(S2Loop::kFull());
+ }
+ // This code implements Andrew's monotone chain algorithm, which is a simple
+ // variant of the Graham scan. Rather than sorting by x-coordinate, instead
+ // we sort the points in CCW order around an origin O such that all points
+ // are guaranteed to be on one side of some geodesic through O. This
+ // ensures that as we scan through the points, each new point can only
+ // belong at the end of the chain (i.e., the chain is monotone in terms of
+ // the angle around O from the starting point).
+ S2Point origin = cap.center().Ortho();
+ std::sort(points_.begin(), points_.end(), OrderedCcwAround(origin));
+
+ // Remove duplicates. We need to do this before checking whether there are
+ // fewer than 3 points.
+ points_.erase(std::unique(points_.begin(), points_.end()), points_.end());
+
+ // Special cases for fewer than 3 points.
+ if (points_.size() < 3) {
+ if (points_.empty()) {
+ return make_unique<S2Loop>(S2Loop::kEmpty());
+ } else if (points_.size() == 1) {
+ return GetSinglePointLoop(points_[0]);
+ } else {
+ return GetSingleEdgeLoop(points_[0], points_[1]);
+ }
+ }
+
+ // Verify that all points lie within a 180 degree span around the origin.
+ S2_DCHECK_GE(s2pred::Sign(origin, points_.front(), points_.back()), 0);
+
+ // Generate the lower and upper halves of the convex hull. Each half
+ // consists of the maximal subset of vertices such that the edge chain makes
+ // only left (CCW) turns.
+ vector<S2Point> lower, upper;
+ GetMonotoneChain(&lower);
+ std::reverse(points_.begin(), points_.end());
+ GetMonotoneChain(&upper);
+
+ // Remove the duplicate vertices and combine the chains.
+ S2_DCHECK_EQ(lower.front(), upper.back());
+ S2_DCHECK_EQ(lower.back(), upper.front());
+ lower.pop_back();
+ upper.pop_back();
+ lower.insert(lower.end(), upper.begin(), upper.end());
+ return make_unique<S2Loop>(lower);
+}
+
+// Iterate through the given points, selecting the maximal subset of points
+// such that the edge chain makes only left (CCW) turns.
+void S2ConvexHullQuery::GetMonotoneChain(vector<S2Point>* output) {
+ S2_DCHECK(output->empty());
+ for (const S2Point& p : points_) {
+ // Remove any points that would cause the chain to make a clockwise turn.
+ while (output->size() >= 2 &&
+ s2pred::Sign(output->end()[-2], output->back(), p) <= 0) {
+ output->pop_back();
+ }
+ output->push_back(p);
+ }
+}
+
+unique_ptr<S2Loop> S2ConvexHullQuery::GetSinglePointLoop(const S2Point& p) {
+ // Construct a 3-vertex polygon consisting of "p" and two nearby vertices.
+ // Note that Contains(p) may be false for the resulting loop (see comments
+ // in header file).
+ static const double kOffset = 1e-15;
+ S2Point d0 = S2::Ortho(p);
+ S2Point d1 = p.CrossProd(d0);
+ vector<S2Point> vertices;
+ vertices.push_back(p);
+ vertices.push_back((p + kOffset * d0).Normalize());
+ vertices.push_back((p + kOffset * d1).Normalize());
+ return make_unique<S2Loop>(vertices);
+}
+
+unique_ptr<S2Loop> S2ConvexHullQuery::GetSingleEdgeLoop(const S2Point& a,
+ const S2Point& b) {
+ // Construct a loop consisting of the two vertices and their midpoint.
+ vector<S2Point> vertices;
+ vertices.push_back(a);
+ vertices.push_back(b);
+ vertices.push_back((a + b).Normalize());
+ auto loop = make_unique<S2Loop>(vertices);
+ // The resulting loop may be clockwise, so invert it if necessary.
+ loop->Normalize();
+ return loop;
+}
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2coords.h"
+
+#include "s2/util/bits/bits.h"
+
+namespace S2 {
+
+namespace internal {
+
+// Define the "extern" constants in s2coords_internal.h.
+
+static_assert(kSwapMask == 0x01 && kInvertMask == 0x02, "masks changed");
+
+// kIJtoPos[orientation][ij] -> pos
+const int kIJtoPos[4][4] = {
+ // (0,0) (0,1) (1,0) (1,1)
+ { 0, 1, 3, 2 }, // canonical order
+ { 0, 3, 1, 2 }, // axes swapped
+ { 2, 3, 1, 0 }, // bits inverted
+ { 2, 1, 3, 0 }, // swapped & inverted
+};
+
+// kPosToIJ[orientation][pos] -> ij
+const int kPosToIJ[4][4] = {
+ // 0 1 2 3
+ { 0, 1, 3, 2 }, // canonical order: (0,0), (0,1), (1,1), (1,0)
+ { 0, 2, 3, 1 }, // axes swapped: (0,0), (1,0), (1,1), (0,1)
+ { 3, 2, 0, 1 }, // bits inverted: (1,1), (1,0), (0,0), (0,1)
+ { 3, 1, 0, 2 }, // swapped & inverted: (1,1), (0,1), (0,0), (1,0)
+};
+
+// kPosToOrientation[pos] -> orientation_modifier
+const int kPosToOrientation[4] = {
+ kSwapMask,
+ 0,
+ 0,
+ kInvertMask + kSwapMask,
+};
+
+const int kFaceUVWFaces[6][3][2] = {
+ { { 4, 1 }, { 5, 2 }, { 3, 0 } },
+ { { 0, 3 }, { 5, 2 }, { 4, 1 } },
+ { { 0, 3 }, { 1, 4 }, { 5, 2 } },
+ { { 2, 5 }, { 1, 4 }, { 0, 3 } },
+ { { 2, 5 }, { 3, 0 }, { 1, 4 } },
+ { { 4, 1 }, { 3, 0 }, { 2, 5 } }
+};
+
+const double kFaceUVWAxes[6][3][3] = {
+ {
+ { 0, 1, 0 },
+ { 0, 0, 1 },
+ { 1, 0, 0 }
+ },
+ {
+ {-1, 0, 0 },
+ { 0, 0, 1 },
+ { 0, 1, 0 }
+ },
+ {
+ {-1, 0, 0 },
+ { 0, -1, 0 },
+ { 0, 0, 1 }
+ },
+ {
+ { 0, 0, -1 },
+ { 0, -1, 0 },
+ {-1, 0, 0 }
+ },
+ {
+ { 0, 0, -1 },
+ { 1, 0, 0 },
+ { 0, -1, 0 }
+ },
+ {
+ { 0, 1, 0 },
+ { 1, 0, 0 },
+ { 0, 0, -1 }
+ }
+};
+
+} // namespace internal
+
+
+
+S2Point FaceXYZtoUVW(int face, const S2Point& p) {
+ // The result coordinates are simply the dot products of P with the (u,v,w)
+ // axes for the given face (see kFaceUVWAxes).
+ switch (face) {
+ case 0: return S2Point( p.y(), p.z(), p.x());
+ case 1: return S2Point(-p.x(), p.z(), p.y());
+ case 2: return S2Point(-p.x(), -p.y(), p.z());
+ case 3: return S2Point(-p.z(), -p.y(), -p.x());
+ case 4: return S2Point(-p.z(), p.x(), -p.y());
+ default: return S2Point( p.y(), p.x(), -p.z());
+ }
+}
+
+int XYZtoFaceSiTi(const S2Point& p, int* face, unsigned int* si,
+ unsigned int* ti) {
+ double u, v;
+ *face = XYZtoFaceUV(p, &u, &v);
+ *si = STtoSiTi(UVtoST(u));
+ *ti = STtoSiTi(UVtoST(v));
+ // If the levels corresponding to si,ti are not equal, then p is not a cell
+ // center. The si,ti values 0 and kMaxSiTi need to be handled specially
+ // because they do not correspond to cell centers at any valid level; they
+ // are mapped to level -1 by the code below.
+ int level = kMaxCellLevel - Bits::FindLSBSetNonZero(*si | kMaxSiTi);
+ if (level < 0 ||
+ level != kMaxCellLevel - Bits::FindLSBSetNonZero(*ti | kMaxSiTi)) {
+ return -1;
+ }
+ S2_DCHECK_LE(level, kMaxCellLevel);
+ // In infinite precision, this test could be changed to ST == SiTi. However,
+ // due to rounding errors, UVtoST(XYZtoFaceUV(FaceUVtoXYZ(STtoUV(...)))) is
+ // not idempotent. On the other hand, center_raw is computed exactly the same
+ // way p was originally computed (if it is indeed the center of an S2Cell):
+ // the comparison can be exact.
+ S2Point center = FaceSiTitoXYZ(*face, *si, *ti).Normalize();
+ return p == center ? level : -1;
+}
+
+S2Point FaceSiTitoXYZ(int face, unsigned int si, unsigned int ti) {
+ double u = STtoUV(SiTitoST(si));
+ double v = STtoUV(SiTitoST(ti));
+ return FaceUVtoXYZ(face, u, v);
+}
+
+} // namespace S2
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2crossing_edge_query.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "s2/base/logging.h"
+#include "s2/r1interval.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2edge_clipping.h"
+#include "s2/s2edge_crosser.h"
+#include "s2/s2shapeutil_count_edges.h"
+
+using s2shapeutil::ShapeEdge;
+using s2shapeutil::ShapeEdgeId;
+using std::vector;
+
+// For small loops it is faster to use brute force. The threshold below was
+// determined using the benchmarks in the unit test.
+static const int kMaxBruteForceEdges = 27;
+
+S2CrossingEdgeQuery::S2CrossingEdgeQuery() {
+}
+
+S2CrossingEdgeQuery::~S2CrossingEdgeQuery() {
+}
+
+void S2CrossingEdgeQuery::Init(const S2ShapeIndex* index) {
+ index_ = index;
+ iter_.Init(index);
+}
+
+vector<s2shapeutil::ShapeEdge> S2CrossingEdgeQuery::GetCrossingEdges(
+ const S2Point& a0, const S2Point& a1, CrossingType type) {
+ vector<s2shapeutil::ShapeEdge> edges;
+ GetCrossingEdges(a0, a1, type, &edges);
+ return edges;
+}
+
+vector<s2shapeutil::ShapeEdge> S2CrossingEdgeQuery::GetCrossingEdges(
+ const S2Point& a0, const S2Point& a1, const S2Shape& shape,
+ CrossingType type) {
+ vector<s2shapeutil::ShapeEdge> edges;
+ GetCrossingEdges(a0, a1, shape, type, &edges);
+ return edges;
+}
+
+void S2CrossingEdgeQuery::GetCrossingEdges(
+ const S2Point& a0, const S2Point& a1, CrossingType type,
+ vector<ShapeEdge>* edges) {
+ edges->clear();
+ GetCandidates(a0, a1, &tmp_candidates_);
+ int min_sign = (type == CrossingType::ALL) ? 0 : 1;
+ S2CopyingEdgeCrosser crosser(a0, a1);
+ int shape_id = -1;
+ const S2Shape* shape = nullptr;
+ for (ShapeEdgeId candidate : tmp_candidates_) {
+ if (candidate.shape_id != shape_id) {
+ shape_id = candidate.shape_id;
+ shape = index_->shape(shape_id);
+ }
+ int edge_id = candidate.edge_id;
+ S2Shape::Edge b = shape->edge(edge_id);
+ if (crosser.CrossingSign(b.v0, b.v1) >= min_sign) {
+ edges->push_back(ShapeEdge(shape_id, edge_id, b));
+ }
+ }
+}
+
+void S2CrossingEdgeQuery::GetCrossingEdges(
+ const S2Point& a0, const S2Point& a1, const S2Shape& shape,
+ CrossingType type, vector<ShapeEdge>* edges) {
+ edges->clear();
+ GetCandidates(a0, a1, shape, &tmp_candidates_);
+ int min_sign = (type == CrossingType::ALL) ? 0 : 1;
+ S2CopyingEdgeCrosser crosser(a0, a1);
+ for (ShapeEdgeId candidate : tmp_candidates_) {
+ int edge_id = candidate.edge_id;
+ S2Shape::Edge b = shape.edge(edge_id);
+ if (crosser.CrossingSign(b.v0, b.v1) >= min_sign) {
+ edges->push_back(ShapeEdge(shape.id(), edge_id, b));
+ }
+ }
+}
+
+vector<ShapeEdgeId> S2CrossingEdgeQuery::GetCandidates(
+ const S2Point& a0, const S2Point& a1) {
+ vector<ShapeEdgeId> edges;
+ GetCandidates(a0, a1, &edges);
+ return edges;
+}
+
+vector<ShapeEdgeId> S2CrossingEdgeQuery::GetCandidates(
+ const S2Point& a0, const S2Point& a1, const S2Shape& shape) {
+ vector<ShapeEdgeId> edges;
+ GetCandidates(a0, a1, shape, &edges);
+ return edges;
+}
+
+void S2CrossingEdgeQuery::GetCandidates(const S2Point& a0, const S2Point& a1,
+ vector<ShapeEdgeId>* edges) {
+ edges->clear();
+ int num_edges = s2shapeutil::CountEdgesUpTo(*index_, kMaxBruteForceEdges + 1);
+ if (num_edges <= kMaxBruteForceEdges) {
+ edges->reserve(num_edges);
+ }
+ VisitRawCandidates(a0, a1, [edges](ShapeEdgeId id) {
+ edges->push_back(id);
+ return true;
+ });
+ if (edges->size() > 1) {
+ std::sort(edges->begin(), edges->end());
+ edges->erase(std::unique(edges->begin(), edges->end()), edges->end());
+ }
+}
+
+void S2CrossingEdgeQuery::GetCandidates(const S2Point& a0, const S2Point& a1,
+ const S2Shape& shape,
+ vector<ShapeEdgeId>* edges) {
+ edges->clear();
+ int num_edges = shape.num_edges();
+ if (num_edges <= kMaxBruteForceEdges) {
+ edges->reserve(num_edges);
+ }
+ VisitRawCandidates(a0, a1, shape, [edges](ShapeEdgeId id) {
+ edges->push_back(id);
+ return true;
+ });
+ if (edges->size() > 1) {
+ std::sort(edges->begin(), edges->end());
+ edges->erase(std::unique(edges->begin(), edges->end()), edges->end());
+ }
+}
+
+bool S2CrossingEdgeQuery::VisitRawCandidates(
+ const S2Point& a0, const S2Point& a1, const ShapeEdgeIdVisitor& visitor) {
+ int num_edges = s2shapeutil::CountEdgesUpTo(*index_, kMaxBruteForceEdges + 1);
+ if (num_edges <= kMaxBruteForceEdges) {
+ int num_shape_ids = index_->num_shape_ids();
+ for (int s = 0; s < num_shape_ids; ++s) {
+ const S2Shape* shape = index_->shape(s);
+ if (shape == nullptr) continue;
+ int num_shape_edges = shape->num_edges();
+ for (int e = 0; e < num_shape_edges; ++e) {
+ if (!visitor(ShapeEdgeId(s, e))) return false;
+ }
+ }
+ return true;
+ }
+ return VisitCells(a0, a1, [&visitor](const S2ShapeIndexCell& cell) {
+ for (int s = 0; s < cell.num_clipped(); ++s) {
+ const S2ClippedShape& clipped = cell.clipped(s);
+ for (int j = 0; j < clipped.num_edges(); ++j) {
+ if (!visitor(ShapeEdgeId(clipped.shape_id(), clipped.edge(j)))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ });
+}
+
+bool S2CrossingEdgeQuery::VisitRawCandidates(
+ const S2Point& a0, const S2Point& a1, const S2Shape& shape,
+ const ShapeEdgeIdVisitor& visitor) {
+ int num_edges = shape.num_edges();
+ if (num_edges <= kMaxBruteForceEdges) {
+ for (int e = 0; e < num_edges; ++e) {
+ if (!visitor(ShapeEdgeId(shape.id(), e))) return false;
+ }
+ return true;
+ }
+ return VisitCells(a0, a1, [&shape, &visitor](const S2ShapeIndexCell& cell) {
+ const S2ClippedShape* clipped = cell.find_clipped(shape.id());
+ if (clipped == nullptr) return true;
+ for (int j = 0; j < clipped->num_edges(); ++j) {
+ if (!visitor(ShapeEdgeId(shape.id(), clipped->edge(j)))) return false;
+ }
+ return true;
+ });
+}
+
+bool S2CrossingEdgeQuery::VisitCells(const S2Point& a0, const S2Point& a1,
+ const CellVisitor& visitor) {
+ visitor_ = &visitor;
+ S2::FaceSegmentVector segments;
+ S2::GetFaceSegments(a0, a1, &segments);
+ for (const auto& segment : segments) {
+ a0_ = segment.a;
+ a1_ = segment.b;
+
+ // Optimization: rather than always starting the recursive subdivision at
+ // the top level face cell, instead we start at the smallest S2CellId that
+ // contains the edge (the "edge root cell"). This typically lets us skip
+ // quite a few levels of recursion since most edges are short.
+ R2Rect edge_bound = R2Rect::FromPointPair(a0_, a1_);
+ S2PaddedCell pcell(S2CellId::FromFace(segment.face), 0);
+ S2CellId edge_root = pcell.ShrinkToFit(edge_bound);
+
+ // Now we need to determine how the edge root cell is related to the cells
+ // in the spatial index (cell_map_). There are three cases:
+ //
+ // 1. edge_root is an index cell or is contained within an index cell.
+ // In this case we only need to look at the contents of that cell.
+ // 2. edge_root is subdivided into one or more index cells. In this case
+ // we recursively subdivide to find the cells intersected by a0a1.
+ // 3. edge_root does not intersect any index cells. In this case there
+ // is nothing to do.
+ S2ShapeIndex::CellRelation relation = iter_.Locate(edge_root);
+ if (relation == S2ShapeIndex::INDEXED) {
+ // edge_root is an index cell or is contained by an index cell (case 1).
+ S2_DCHECK(iter_.id().contains(edge_root));
+ if (!visitor(iter_.cell())) return false;
+ } else if (relation == S2ShapeIndex::SUBDIVIDED) {
+ // edge_root is subdivided into one or more index cells (case 2). We
+ // find the cells intersected by a0a1 using recursive subdivision.
+ if (!edge_root.is_face()) pcell = S2PaddedCell(edge_root, 0);
+ if (!VisitCells(pcell, edge_bound)) return false;
+ }
+ }
+ return true;
+}
+
+bool S2CrossingEdgeQuery::VisitCells(
+ const S2Point& a0, const S2Point& a1, const S2PaddedCell& root,
+ const CellVisitor& visitor) {
+ S2_DCHECK_EQ(root.padding(), 0);
+ visitor_ = &visitor;
+ // We use padding when clipping to ensure that the result is non-empty
+ // whenever the edge (a0, a1) intersects the given root cell.
+ if (S2::ClipToPaddedFace(a0, a1, root.id().face(),
+ S2::kFaceClipErrorUVCoord, &a0_, &a1_)) {
+ R2Rect edge_bound = R2Rect::FromPointPair(a0_, a1_);
+ if (root.bound().Intersects(edge_bound)) {
+ return VisitCells(root, edge_bound);
+ }
+ }
+ return true;
+}
+
+// Computes the index cells intersected by the current edge that are
+// descendants of "pcell" and calls visitor_ for each one.
+//
+// WARNING: This function is recursive with a maximum depth of 30. The frame
+// size is about 2K in versions of GCC prior to 4.7 due to poor overlapping
+// of storage for temporaries. This is fixed in GCC 4.7, reducing the frame
+// size to about 350 bytes (i.e., worst-case total stack usage of about 10K).
+bool S2CrossingEdgeQuery::VisitCells(const S2PaddedCell& pcell,
+ const R2Rect& edge_bound) {
+ // This code uses S2PaddedCell because it has the methods we need for
+ // efficient splitting, however the actual padding is required to be zero.
+ S2_DCHECK_EQ(pcell.padding(), 0);
+
+ iter_.Seek(pcell.id().range_min());
+ if (iter_.done() || iter_.id() > pcell.id().range_max()) {
+ // The index does not contain "pcell" or any of its descendants.
+ return true;
+ }
+ if (iter_.id() == pcell.id()) {
+ return (*visitor_)(iter_.cell());
+ }
+
+ // Otherwise, split the edge among the four children of "pcell".
+ R2Point center = pcell.middle().lo();
+ if (edge_bound[0].hi() < center[0]) {
+ // Edge is entirely contained in the two left children.
+ return ClipVAxis(edge_bound, center[1], 0, pcell);
+ } else if (edge_bound[0].lo() >= center[0]) {
+ // Edge is entirely contained in the two right children.
+ return ClipVAxis(edge_bound, center[1], 1, pcell);
+ } else {
+ R2Rect child_bounds[2];
+ SplitUBound(edge_bound, center[0], child_bounds);
+ if (edge_bound[1].hi() < center[1]) {
+ // Edge is entirely contained in the two lower children.
+ return (VisitCells(S2PaddedCell(pcell, 0, 0), child_bounds[0]) &&
+ VisitCells(S2PaddedCell(pcell, 1, 0), child_bounds[1]));
+ } else if (edge_bound[1].lo() >= center[1]) {
+ // Edge is entirely contained in the two upper children.
+ return (VisitCells(S2PaddedCell(pcell, 0, 1), child_bounds[0]) &&
+ VisitCells(S2PaddedCell(pcell, 1, 1), child_bounds[1]));
+ } else {
+ // The edge bound spans all four children. The edge itself intersects
+ // at most three children (since no padding is being used).
+ return (ClipVAxis(child_bounds[0], center[1], 0, pcell) &&
+ ClipVAxis(child_bounds[1], center[1], 1, pcell));
+ }
+ }
+}
+
+// Given either the left (i=0) or right (i=1) side of a padded cell "pcell",
+// determine whether the current edge intersects the lower child, upper child,
+// or both children, and call VisitCells() recursively on those children.
+// "center" is the v-coordinate at the center of "pcell".
+inline bool S2CrossingEdgeQuery::ClipVAxis(const R2Rect& edge_bound,
+ double center, int i,
+ const S2PaddedCell& pcell) {
+ if (edge_bound[1].hi() < center) {
+ // Edge is entirely contained in the lower child.
+ return VisitCells(S2PaddedCell(pcell, i, 0), edge_bound);
+ } else if (edge_bound[1].lo() >= center) {
+ // Edge is entirely contained in the upper child.
+ return VisitCells(S2PaddedCell(pcell, i, 1), edge_bound);
+ } else {
+ // The edge intersects both children.
+ R2Rect child_bounds[2];
+ SplitVBound(edge_bound, center, child_bounds);
+ return (VisitCells(S2PaddedCell(pcell, i, 0), child_bounds[0]) &&
+ VisitCells(S2PaddedCell(pcell, i, 1), child_bounds[1]));
+ }
+}
+
+// Split the current edge into two child edges at the given u-value "u" and
+// return the bound for each child.
+void S2CrossingEdgeQuery::SplitUBound(const R2Rect& edge_bound, double u,
+ R2Rect child_bounds[2]) const {
+ // See comments in MutableS2ShapeIndex::ClipUBound.
+ double v = edge_bound[1].Project(
+ S2::InterpolateDouble(u, a0_[0], a1_[0], a0_[1], a1_[1]));
+
+ // "diag_" indicates which diagonal of the bounding box is spanned by a0a1:
+ // it is 0 if a0a1 has positive slope, and 1 if a0a1 has negative slope.
+ int diag = (a0_[0] > a1_[0]) != (a0_[1] > a1_[1]);
+ SplitBound(edge_bound, 0, u, diag, v, child_bounds);
+}
+
+// Split the current edge into two child edges at the given v-value "v" and
+// return the bound for each child.
+void S2CrossingEdgeQuery::SplitVBound(const R2Rect& edge_bound, double v,
+ R2Rect child_bounds[2]) const {
+ double u = edge_bound[0].Project(
+ S2::InterpolateDouble(v, a0_[1], a1_[1], a0_[0], a1_[0]));
+ int diag = (a0_[0] > a1_[0]) != (a0_[1] > a1_[1]);
+ SplitBound(edge_bound, diag, u, 0, v, child_bounds);
+}
+
+// Split the current edge into two child edges at the given point (u,v) and
+// return the bound for each child. "u_end" and "v_end" indicate which bound
+// endpoints of child 1 will be updated.
+inline void S2CrossingEdgeQuery::SplitBound(const R2Rect& edge_bound, int u_end,
+ double u, int v_end, double v,
+ R2Rect child_bounds[2]) {
+ child_bounds[0] = edge_bound;
+ child_bounds[0][0][1 - u_end] = u;
+ child_bounds[0][1][1 - v_end] = v;
+ S2_DCHECK(!child_bounds[0].is_empty());
+ S2_DCHECK(edge_bound.Contains(child_bounds[0]));
+
+ child_bounds[1] = edge_bound;
+ child_bounds[1][0][u_end] = u;
+ child_bounds[1][1][v_end] = v;
+ S2_DCHECK(!child_bounds[1].is_empty());
+ S2_DCHECK(edge_bound.Contains(child_bounds[1]));
+}
+
+void S2CrossingEdgeQuery::GetCells(const S2Point& a0, const S2Point& a1,
+ const S2PaddedCell& root,
+ vector<const S2ShapeIndexCell*>* cells) {
+ cells->clear();
+ VisitCells(a0, a1, root, [cells](const S2ShapeIndexCell& cell) {
+ cells->push_back(&cell);
+ return true;
+ });
+}
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2debug.h"
+
+#include "s2/base/logging.h"
+
+DEFINE_bool(s2debug, !!google::DEBUG_MODE,
+ "Enable automatic validity checking in S2 code");
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "s2/s2earth.h"
+
+#include <cmath>
+#include <algorithm>
+
+namespace {
+
+// http://en.wikipedia.org/wiki/Haversine_formula
+// Haversine(x) has very good numerical stability around zero.
+// Haversine(x) == (1-cos(x))/2 == sin(x/2)^2; must be implemented with the
+// second form to reap the numerical benefits.
+double Haversine(const double radians) {
+ const double sinHalf = sin(radians / 2);
+ return sinHalf * sinHalf;
+}
+
+} // namespace
+
+double S2Earth::ToLongitudeRadians(const util::units::Meters& distance,
+ double latitude_radians) {
+ double scalar = cos(latitude_radians);
+ if (scalar == 0) return M_PI * 2;
+ return std::min(ToRadians(distance) / scalar, M_PI * 2);
+}
+
+// Sourced from http://www.movable-type.co.uk/scripts/latlong.html.
+S1Angle S2Earth::GetInitialBearing(const S2LatLng& a, const S2LatLng& b) {
+ const double lat1 = a.lat().radians();
+ const double cosLat2 = cos(b.lat().radians());
+ const double lat_diff = b.lat().radians() - a.lat().radians();
+ const double lng_diff = b.lng().radians() - a.lng().radians();
+
+ const double x =
+ sin(lat_diff) + sin(lat1) * cosLat2 * 2 * Haversine(lng_diff);
+ const double y = sin(lng_diff) * cosLat2;
+ return S1Angle::Radians(atan2(y, x));
+}
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2edge_clipping.h"
+
+#include <cfloat>
+#include <cmath>
+
+#include "s2/base/logging.h"
+#include "s2/r1interval.h"
+#include "s2/s2coords.h"
+#include "s2/s2pointutil.h"
+#include "s2/util/math/vector.h"
+
+namespace S2 {
+
+using std::fabs;
+using std::max;
+using std::min;
+
+// Error constant definitions. See the header file for details.
+const double kFaceClipErrorRadians = 3 * DBL_EPSILON;
+const double kFaceClipErrorUVDist = 9 * DBL_EPSILON;
+const double kFaceClipErrorUVCoord = 9 * M_SQRT1_2 * DBL_EPSILON;
+const double kIntersectsRectErrorUVDist = 3 * M_SQRT2 * DBL_EPSILON;
+const double kEdgeClipErrorUVCoord = 2.25 * DBL_EPSILON;
+const double kEdgeClipErrorUVDist = 2.25 * DBL_EPSILON;
+
+// S2PointUVW is used to document that a given S2Point is expressed in the
+// (u,v,w) coordinates of some cube face.
+using S2PointUVW = S2Point;
+
+// The three functions below all compare a sum (u + v) to a third value w.
+// They are implemented in such a way that they produce an exact result even
+// though all calculations are done with ordinary floating-point operations.
+// Here are the principles on which these functions are based:
+//
+// A. If u + v < w in floating-point, then u + v < w in exact arithmetic.
+//
+// B. If u + v < w in exact arithmetic, then at least one of the following
+// expressions is true in floating-point:
+// u + v < w
+// u < w - v
+// v < w - u
+//
+// Proof: By rearranging terms and substituting ">" for "<", we can assume
+// that all values are non-negative. Now clearly "w" is not the smallest
+// value, so assume WLOG that "u" is the smallest. We want to show that
+// u < w - v in floating-point. If v >= w/2, the calculation of w - v is
+// exact since the result is smaller in magnitude than either input value,
+// so the result holds. Otherwise we have u <= v < w/2 and w - v >= w/2
+// (even in floating point), so the result also holds.
+
+// Return true if u + v == w exactly.
+inline static bool SumEquals(double u, double v, double w) {
+ return (u + v == w) && (u == w - v) && (v == w - u);
+}
+
+// Return true if a given directed line L intersects the cube face F. The
+// line L is defined by its normal N in the (u,v,w) coordinates of F.
+inline static bool IntersectsFace(const S2PointUVW& n) {
+ // L intersects the [-1,1]x[-1,1] square in (u,v) if and only if the dot
+ // products of N with the four corner vertices (-1,-1,1), (1,-1,1), (1,1,1),
+ // and (-1,1,1) do not all have the same sign. This is true exactly when
+ // |Nu| + |Nv| >= |Nw|. The code below evaluates this expression exactly
+ // (see comments above).
+ double u = fabs(n[0]), v = fabs(n[1]), w = fabs(n[2]);
+ // We only need to consider the cases where u or v is the smallest value,
+ // since if w is the smallest then both expressions below will have a
+ // positive LHS and a negative RHS.
+ return (v >= w - u) && (u >= w - v);
+}
+
+// Given a directed line L intersecting a cube face F, return true if L
+// intersects two opposite edges of F (including the case where L passes
+// exactly through a corner vertex of F). The line L is defined by its
+// normal N in the (u,v,w) coordinates of F.
+inline static bool IntersectsOppositeEdges(const S2PointUVW& n) {
+ // The line L intersects opposite edges of the [-1,1]x[-1,1] (u,v) square if
+ // and only exactly two of the corner vertices lie on each side of L. This
+ // is true exactly when ||Nu| - |Nv|| >= |Nw|. The code below evaluates this
+ // expression exactly (see comments above).
+ double u = fabs(n[0]), v = fabs(n[1]), w = fabs(n[2]);
+ // If w is the smallest, the following line returns an exact result.
+ if (fabs(u - v) != w) return fabs(u - v) >= w;
+ // Otherwise u - v = w exactly, or w is not the smallest value. In either
+ // case the following line returns the correct result.
+ return (u >= v) ? (u - w >= v) : (v - w >= u);
+}
+
+// Given cube face F and a directed line L (represented by its CCW normal N in
+// the (u,v,w) coordinates of F), compute the axis of the cube face edge where
+// L exits the face: return 0 if L exits through the u=-1 or u=+1 edge, and 1
+// if L exits through the v=-1 or v=+1 edge. Either result is acceptable if L
+// exits exactly through a corner vertex of the cube face.
+static int GetExitAxis(const S2PointUVW& n) {
+ S2_DCHECK(IntersectsFace(n));
+ if (IntersectsOppositeEdges(n)) {
+ // The line passes through through opposite edges of the face.
+ // It exits through the v=+1 or v=-1 edge if the u-component of N has a
+ // larger absolute magnitude than the v-component.
+ return (fabs(n[0]) >= fabs(n[1])) ? 1 : 0;
+ } else {
+ // The line passes through through two adjacent edges of the face.
+ // It exits the v=+1 or v=-1 edge if an even number of the components of N
+ // are negative. We test this using signbit() rather than multiplication
+ // to avoid the possibility of underflow.
+ S2_DCHECK(n[0] != 0 && n[1] != 0 && n[2] != 0);
+ using std::signbit;
+ return ((signbit(n[0]) ^ signbit(n[1]) ^ signbit(n[2])) == 0) ? 1 : 0;
+ }
+}
+
+// Given a cube face F, a directed line L (represented by its CCW normal N in
+// the (u,v,w) coordinates of F), and result of GetExitAxis(N), return the
+// (u,v) coordinates of the point where L exits the cube face.
+static R2Point GetExitPoint(const S2PointUVW& n, int axis) {
+ if (axis == 0) {
+ double u = (n[1] > 0) ? 1.0 : -1.0;
+ return R2Point(u, (-u * n[0] - n[2]) / n[1]);
+ } else {
+ double v = (n[0] < 0) ? 1.0 : -1.0;
+ return R2Point((-v * n[1] - n[2]) / n[0], v);
+ }
+}
+
+// Given a line segment AB whose origin A has been projected onto a given cube
+// face, determine whether it is necessary to project A onto a different face
+// instead. This can happen because the normal of the line AB is not computed
+// exactly, so that the line AB (defined as the set of points perpendicular to
+// the normal) may not intersect the cube face containing A. Even if it does
+// intersect the face, the "exit point" of the line from that face may be on
+// the wrong side of A (i.e., in the direction away from B). If this happens,
+// we reproject A onto the adjacent face where the line AB approaches A most
+// closely. This moves the origin by a small amount, but never more than the
+// error tolerances documented in the header file.
+static int MoveOriginToValidFace(int face, const S2Point& a,
+ const S2Point& ab, R2Point* a_uv) {
+ // Fast path: if the origin is sufficiently far inside the face, it is
+ // always safe to use it.
+ const double kMaxSafeUVCoord = 1 - kFaceClipErrorUVCoord;
+ if (max(fabs((*a_uv)[0]), fabs((*a_uv)[1])) <= kMaxSafeUVCoord) {
+ return face;
+ }
+ // Otherwise check whether the normal AB even intersects this face.
+ S2PointUVW n = S2::FaceXYZtoUVW(face, ab);
+ if (IntersectsFace(n)) {
+ // Check whether the point where the line AB exits this face is on the
+ // wrong side of A (by more than the acceptable error tolerance).
+ S2Point exit = S2::FaceUVtoXYZ(face, GetExitPoint(n, GetExitAxis(n)));
+ S2Point a_tangent = ab.Normalize().CrossProd(a);
+ if ((exit - a).DotProd(a_tangent) >= -kFaceClipErrorRadians) {
+ return face; // We can use the given face.
+ }
+ }
+ // Otherwise we reproject A to the nearest adjacent face. (If line AB does
+ // not pass through a given face, it must pass through all adjacent faces.)
+ if (fabs((*a_uv)[0]) >= fabs((*a_uv)[1])) {
+ face = S2::GetUVWFace(face, 0 /*U axis*/, (*a_uv)[0] > 0);
+ } else {
+ face = S2::GetUVWFace(face, 1 /*V axis*/, (*a_uv)[1] > 0);
+ }
+ S2_DCHECK(IntersectsFace(S2::FaceXYZtoUVW(face, ab)));
+ S2::ValidFaceXYZtoUV(face, a, a_uv);
+ (*a_uv)[0] = max(-1.0, min(1.0, (*a_uv)[0]));
+ (*a_uv)[1] = max(-1.0, min(1.0, (*a_uv)[1]));
+ return face;
+}
+
+// Return the next face that should be visited by GetFaceSegments, given that
+// we have just visited "face" and we are following the line AB (represented
+// by its normal N in the (u,v,w) coordinates of that face). The other
+// arguments include the point where AB exits "face", the corresponding
+// exit axis, and the "target face" containing the destination point B.
+static int GetNextFace(int face, const R2Point& exit, int axis,
+ const S2PointUVW& n, int target_face) {
+ // We return the face that is adjacent to the exit point along the given
+ // axis. If line AB exits *exactly* through a corner of the face, there are
+ // two possible next faces. If one is the "target face" containing B, then
+ // we guarantee that we advance to that face directly.
+ //
+ // The three conditions below check that (1) AB exits approximately through
+ // a corner, (2) the adjacent face along the non-exit axis is the target
+ // face, and (3) AB exits *exactly* through the corner. (The SumEquals()
+ // code checks whether the dot product of (u,v,1) and "n" is exactly zero.)
+ if (fabs(exit[1 - axis]) == 1 &&
+ S2::GetUVWFace(face, 1 - axis, exit[1 - axis] > 0) == target_face &&
+ SumEquals(exit[0] * n[0], exit[1] * n[1], -n[2])) {
+ return target_face;
+ }
+ // Otherwise return the face that is adjacent to the exit point in the
+ // direction of the exit axis.
+ return S2::GetUVWFace(face, axis, exit[axis] > 0);
+}
+
+void GetFaceSegments(const S2Point& a, const S2Point& b,
+ FaceSegmentVector* segments) {
+ S2_DCHECK(S2::IsUnitLength(a));
+ S2_DCHECK(S2::IsUnitLength(b));
+ segments->clear();
+
+ // Fast path: both endpoints are on the same face.
+ FaceSegment segment;
+ int a_face = S2::XYZtoFaceUV(a, &segment.a);
+ int b_face = S2::XYZtoFaceUV(b, &segment.b);
+ if (a_face == b_face) {
+ segment.face = a_face;
+ segments->push_back(segment);
+ return;
+ }
+ // Starting at A, we follow AB from face to face until we reach the face
+ // containing B. The following code is designed to ensure that we always
+ // reach B, even in the presence of numerical errors.
+ //
+ // First we compute the normal to the plane containing A and B. This normal
+ // becomes the ultimate definition of the line AB; it is used to resolve all
+ // questions regarding where exactly the line goes. Unfortunately due to
+ // numerical errors, the line may not quite intersect the faces containing
+ // the original endpoints. We handle this by moving A and/or B slightly if
+ // necessary so that they are on faces intersected by the line AB.
+ S2Point ab = S2::RobustCrossProd(a, b);
+ a_face = MoveOriginToValidFace(a_face, a, ab, &segment.a);
+ b_face = MoveOriginToValidFace(b_face, b, -ab, &segment.b);
+
+ // Now we simply follow AB from face to face until we reach B.
+ segment.face = a_face;
+ R2Point b_saved = segment.b;
+ for (int face = a_face; face != b_face; ) {
+ // Complete the current segment by finding the point where AB exits the
+ // current face.
+ S2PointUVW n = S2::FaceXYZtoUVW(face, ab);
+ int exit_axis = GetExitAxis(n);
+ segment.b = GetExitPoint(n, exit_axis);
+ segments->push_back(segment);
+
+ // Compute the next face intersected by AB, and translate the exit point
+ // of the current segment into the (u,v) coordinates of the next face.
+ // This becomes the first point of the next segment.
+ S2Point exit_xyz = S2::FaceUVtoXYZ(face, segment.b);
+ face = GetNextFace(face, segment.b, exit_axis, n, b_face);
+ S2PointUVW exit_uvw = S2::FaceXYZtoUVW(face, exit_xyz);
+ segment.face = face;
+ segment.a = R2Point(exit_uvw[0], exit_uvw[1]);
+ }
+ // Finish the last segment.
+ segment.b = b_saved;
+ segments->push_back(segment);
+}
+
+// This helper function does two things. First, it clips the line segment AB
+// to find the clipped destination B' on a given face. (The face is specified
+// implicitly by expressing *all arguments* in the (u,v,w) coordinates of that
+// face.) Second, it partially computes whether the segment AB intersects
+// this face at all. The actual condition is fairly complicated, but it turns
+// out that it can be expressed as a "score" that can be computed
+// independently when clipping the two endpoints A and B. This function
+// returns the score for the given endpoint, which is an integer ranging from
+// 0 to 3. If the sum of the two scores is 3 or more, then AB does not
+// intersect this face. See the calling function for the meaning of the
+// various parameters.
+static int ClipDestination(
+ const S2PointUVW& a, const S2PointUVW& b, const S2PointUVW& scaled_n,
+ const S2PointUVW& a_tangent, const S2PointUVW& b_tangent, double scale_uv,
+ R2Point* uv) {
+ S2_DCHECK(IntersectsFace(scaled_n));
+
+ // Optimization: if B is within the safe region of the face, use it.
+ const double kMaxSafeUVCoord = 1 - kFaceClipErrorUVCoord;
+ if (b[2] > 0) {
+ *uv = R2Point(b[0] / b[2], b[1] / b[2]);
+ if (max(fabs((*uv)[0]), fabs((*uv)[1])) <= kMaxSafeUVCoord)
+ return 0;
+ }
+ // Otherwise find the point B' where the line AB exits the face.
+ *uv = scale_uv * GetExitPoint(scaled_n, GetExitAxis(scaled_n));
+ S2PointUVW p((*uv)[0], (*uv)[1], 1.0);
+
+ // Determine if the exit point B' is contained within the segment. We do this
+ // by computing the dot products with two inward-facing tangent vectors at A
+ // and B. If either dot product is negative, we say that B' is on the "wrong
+ // side" of that point. As the point B' moves around the great circle AB past
+ // the segment endpoint B, it is initially on the wrong side of B only; as it
+ // moves further it is on the wrong side of both endpoints; and then it is on
+ // the wrong side of A only. If the exit point B' is on the wrong side of
+ // either endpoint, we can't use it; instead the segment is clipped at the
+ // original endpoint B.
+ //
+ // We reject the segment if the sum of the scores of the two endpoints is 3
+ // or more. Here is what that rule encodes:
+ // - If B' is on the wrong side of A, then the other clipped endpoint A'
+ // must be in the interior of AB (otherwise AB' would go the wrong way
+ // around the circle). There is a similar rule for A'.
+ // - If B' is on the wrong side of either endpoint (and therefore we must
+ // use the original endpoint B instead), then it must be possible to
+ // project B onto this face (i.e., its w-coordinate must be positive).
+ // This rule is only necessary to handle certain zero-length edges (A=B).
+ int score = 0;
+ if ((p - a).DotProd(a_tangent) < 0) {
+ score = 2; // B' is on wrong side of A.
+ } else if ((p - b).DotProd(b_tangent) < 0) {
+ score = 1; // B' is on wrong side of B.
+ }
+ if (score > 0) { // B' is not in the interior of AB.
+ if (b[2] <= 0) {
+ score = 3; // B cannot be projected onto this face.
+ } else {
+ *uv = R2Point(b[0] / b[2], b[1] / b[2]);
+ }
+ }
+ return score;
+}
+
+bool ClipToPaddedFace(const S2Point& a_xyz, const S2Point& b_xyz, int face,
+ double padding, R2Point* a_uv, R2Point* b_uv) {
+ S2_DCHECK_GE(padding, 0);
+ // Fast path: both endpoints are on the given face.
+ if (S2::GetFace(a_xyz) == face && S2::GetFace(b_xyz) == face) {
+ S2::ValidFaceXYZtoUV(face, a_xyz, a_uv);
+ S2::ValidFaceXYZtoUV(face, b_xyz, b_uv);
+ return true;
+ }
+ // Convert everything into the (u,v,w) coordinates of the given face. Note
+ // that the cross product *must* be computed in the original (x,y,z)
+ // coordinate system because RobustCrossProd (unlike the mathematical cross
+ // product) can produce different results in different coordinate systems
+ // when one argument is a linear multiple of the other, due to the use of
+ // symbolic perturbations.
+ S2PointUVW n = S2::FaceXYZtoUVW(face, S2::RobustCrossProd(a_xyz, b_xyz));
+ S2PointUVW a = S2::FaceXYZtoUVW(face, a_xyz);
+ S2PointUVW b = S2::FaceXYZtoUVW(face, b_xyz);
+
+ // Padding is handled by scaling the u- and v-components of the normal.
+ // Letting R=1+padding, this means that when we compute the dot product of
+ // the normal with a cube face vertex (such as (-1,-1,1)), we will actually
+ // compute the dot product with the scaled vertex (-R,-R,1). This allows
+ // methods such as IntersectsFace(), GetExitAxis(), etc, to handle padding
+ // with no further modifications.
+ const double scale_uv = 1 + padding;
+ S2PointUVW scaled_n(scale_uv * n[0], scale_uv * n[1], n[2]);
+ if (!IntersectsFace(scaled_n)) return false;
+
+ // TODO(ericv): This is a temporary hack until I rewrite S2::RobustCrossProd;
+ // it avoids loss of precision in Normalize() when the vector is so small
+ // that it underflows.
+ if (max(fabs(n[0]), max(fabs(n[1]), fabs(n[2]))) < ldexp(1.0, -511)) {
+ n *= ldexp(1.0, 563);
+ } // END OF HACK
+ n = n.Normalize();
+ S2PointUVW a_tangent = n.CrossProd(a);
+ S2PointUVW b_tangent = b.CrossProd(n);
+ // As described above, if the sum of the scores from clipping the two
+ // endpoints is 3 or more, then the segment does not intersect this face.
+ int a_score = ClipDestination(b, a, -scaled_n, b_tangent, a_tangent,
+ scale_uv, a_uv);
+ int b_score = ClipDestination(a, b, scaled_n, a_tangent, b_tangent,
+ scale_uv, b_uv);
+ return a_score + b_score < 3;
+}
+
+bool IntersectsRect(const R2Point& a, const R2Point& b, const R2Rect& rect) {
+ // First check whether the bound of AB intersects "rect".
+ R2Rect bound = R2Rect::FromPointPair(a, b);
+ if (!rect.Intersects(bound)) return false;
+
+ // Otherwise AB intersects "rect" if and only if all four vertices of "rect"
+ // do not lie on the same side of the extended line AB. We test this by
+ // finding the two vertices of "rect" with minimum and maximum projections
+ // onto the normal of AB, and computing their dot products with the edge
+ // normal.
+ R2Point n = (b - a).Ortho();
+ int i = (n[0] >= 0) ? 1 : 0;
+ int j = (n[1] >= 0) ? 1 : 0;
+ double max = n.DotProd(rect.GetVertex(i, j) - a);
+ double min = n.DotProd(rect.GetVertex(1-i, 1-j) - a);
+ return (max >= 0) && (min <= 0);
+}
+
+inline static bool UpdateEndpoint(R1Interval* bound, int end, double value) {
+ if (end == 0) {
+ if (bound->hi() < value) return false;
+ if (bound->lo() < value) bound->set_lo(value);
+ } else {
+ if (bound->lo() > value) return false;
+ if (bound->hi() > value) bound->set_hi(value);
+ }
+ return true;
+}
+
+// Given a line segment from (a0,a1) to (b0,b1) and a bounding interval for
+// each axis, clip the segment further if necessary so that "bound0" does not
+// extend outside the given interval "clip". "diag" is a a precomputed helper
+// variable that indicates which diagonal of the bounding box is spanned by AB:
+// it is 0 if AB has positive slope, and 1 if AB has negative slope.
+inline static bool ClipBoundAxis(double a0, double b0, R1Interval* bound0,
+ double a1, double b1, R1Interval* bound1,
+ int diag, const R1Interval& clip0) {
+ if (bound0->lo() < clip0.lo()) {
+ if (bound0->hi() < clip0.lo()) return false;
+ (*bound0)[0] = clip0.lo();
+ if (!UpdateEndpoint(bound1, diag,
+ InterpolateDouble(clip0.lo(), a0, b0, a1, b1)))
+ return false;
+ }
+ if (bound0->hi() > clip0.hi()) {
+ if (bound0->lo() > clip0.hi()) return false;
+ (*bound0)[1] = clip0.hi();
+ if (!UpdateEndpoint(bound1, 1-diag,
+ InterpolateDouble(clip0.hi(), a0, b0, a1, b1)))
+ return false;
+ }
+ return true;
+}
+
+R2Rect GetClippedEdgeBound(const R2Point& a, const R2Point& b,
+ const R2Rect& clip) {
+ R2Rect bound = R2Rect::FromPointPair(a, b);
+ if (ClipEdgeBound(a, b, clip, &bound)) return bound;
+ return R2Rect::Empty();
+}
+
+bool ClipEdgeBound(const R2Point& a, const R2Point& b, const R2Rect& clip,
+ R2Rect* bound) {
+ // "diag" indicates which diagonal of the bounding box is spanned by AB: it
+ // is 0 if AB has positive slope, and 1 if AB has negative slope. This is
+ // used to determine which interval endpoints need to be updated each time
+ // the edge is clipped.
+ int diag = (a[0] > b[0]) != (a[1] > b[1]);
+ return (ClipBoundAxis(a[0], b[0], &(*bound)[0], a[1], b[1], &(*bound)[1],
+ diag, clip[0]) &&
+ ClipBoundAxis(a[1], b[1], &(*bound)[1], a[0], b[0], &(*bound)[0],
+ diag, clip[1]));
+}
+
+bool ClipEdge(const R2Point& a, const R2Point& b, const R2Rect& clip,
+ R2Point* a_clipped, R2Point* b_clipped) {
+ // Compute the bounding rectangle of AB, clip it, and then extract the new
+ // endpoints from the clipped bound.
+ R2Rect bound = R2Rect::FromPointPair(a, b);
+ if (ClipEdgeBound(a, b, clip, &bound)) {
+ int ai = (a[0] > b[0]), aj = (a[1] > b[1]);
+ *a_clipped = bound.GetVertex(ai, aj);
+ *b_clipped = bound.GetVertex(1-ai, 1-aj);
+ return true;
+ }
+ return false;
+}
+
+} // namespace S2
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2edge_crosser.h"
+
+#include "s2/base/logging.h"
+#include "s2/s2pointutil.h"
+#include "s2/s2predicates.h"
+
+int S2EdgeCrosser::CrossingSignInternal(const S2Point* d) {
+ // Compute the actual result, and then save the current vertex D as the next
+ // vertex C, and save the orientation of the next triangle ACB (which is
+ // opposite to the current triangle BDA).
+ int result = CrossingSignInternal2(*d);
+ c_ = d;
+ acb_ = -bda_;
+ return result;
+}
+
+inline int S2EdgeCrosser::CrossingSignInternal2(const S2Point& d) {
+ // At this point, a very common situation is that A,B,C,D are four points on
+ // a line such that AB does not overlap CD. (For example, this happens when
+ // a line or curve is sampled finely, or when geometry is constructed by
+ // computing the union of S2CellIds.) Most of the time, we can determine
+ // that AB and CD do not intersect by computing the two outward-facing
+ // tangents at A and B (parallel to AB) and testing whether AB and CD are on
+ // opposite sides of the plane perpendicular to one of these tangents. This
+ // is moderately expensive but still much cheaper than s2pred::ExpensiveSign.
+ if (!have_tangents_) {
+ S2Point norm = S2::RobustCrossProd(*a_, *b_).Normalize();
+ a_tangent_ = a_->CrossProd(norm);
+ b_tangent_ = norm.CrossProd(*b_);
+ have_tangents_ = true;
+ }
+ // The error in RobustCrossProd() is insignificant. The maximum error in
+ // the call to CrossProd() (i.e., the maximum norm of the error vector) is
+ // (0.5 + 1/sqrt(3)) * DBL_EPSILON. The maximum error in each call to
+ // DotProd() below is DBL_EPSILON. (There is also a small relative error
+ // term that is insignificant because we are comparing the result against a
+ // constant that is very close to zero.)
+ static const double kError = (1.5 + 1/sqrt(3.0)) * DBL_EPSILON;
+ if ((c_->DotProd(a_tangent_) > kError && d.DotProd(a_tangent_) > kError) ||
+ (c_->DotProd(b_tangent_) > kError && d.DotProd(b_tangent_) > kError)) {
+ return -1;
+ }
+
+ // Otherwise, eliminate the cases where two vertices from different edges
+ // are equal. (These cases could be handled in the code below, but we would
+ // rather avoid calling ExpensiveSign whenever possible.)
+ if (*a_ == *c_ || *a_ == d || *b_ == *c_ || *b_ == d) return 0;
+
+ // Eliminate cases where an input edge is degenerate. (Note that in most
+ // cases, if CD is degenerate then this method is not even called because
+ // acb_ and bda have different signs.)
+ if (*a_ == *b_ || *c_ == d) return -1;
+
+ // Otherwise it's time to break out the big guns.
+ if (acb_ == 0) acb_ = -s2pred::ExpensiveSign(*a_, *b_, *c_);
+ S2_DCHECK_NE(acb_, 0);
+ if (bda_ == 0) bda_ = s2pred::ExpensiveSign(*a_, *b_, d);
+ S2_DCHECK_NE(bda_, 0);
+ if (bda_ != acb_) return -1;
+
+ Vector3_d c_cross_d = c_->CrossProd(d);
+ int cbd = -s2pred::Sign(*c_, d, *b_, c_cross_d);
+ S2_DCHECK_NE(cbd, 0);
+ if (cbd != acb_) return -1;
+ int dac = s2pred::Sign(*c_, d, *a_, c_cross_d);
+ S2_DCHECK_NE(dac, 0);
+ return (dac != acb_) ? -1 : 1;
+}
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2edge_crossings.h"
+#include "s2/s2edge_crossings_internal.h"
+
+#include <cmath>
+
+#include "s2/base/logging.h"
+#include "s2/s1angle.h"
+#include "s2/s2edge_crosser.h"
+#include "s2/s2pointutil.h"
+#include "s2/s2predicates.h"
+#include "s2/s2predicates_internal.h"
+#include "s2/util/math/exactfloat/exactfloat.h"
+
+namespace S2 {
+
+using internal::GetIntersectionExact;
+using internal::IntersectionMethod;
+using internal::intersection_method_tally_;
+using std::fabs;
+using std::sqrt;
+
+// All error bounds in this file are expressed in terms of the maximum
+// rounding error for a floating-point type. The rounding error is half of
+// the numeric_limits<T>::epsilon() value.
+static constexpr double DBL_ERR = s2pred::rounding_epsilon<double>();
+
+// kIntersectionError can be set somewhat arbitrarily, because the algorithm
+// uses more precision when necessary in order to achieve the specified error.
+// The only strict requirement is that kIntersectionError >= 2 * DBL_ERR
+// radians. However, using a larger error tolerance makes the algorithm more
+// efficient because it reduces the number of cases where exact arithmetic is
+// needed.
+const S1Angle kIntersectionError = S1Angle::Radians(8 * DBL_ERR);
+
+const S1Angle kIntersectionMergeRadius = 2 * kIntersectionError;
+
+namespace internal {
+
+const S1Angle kIntersectionExactError = S1Angle::Radians(2 * DBL_ERR);
+
+int* intersection_method_tally_ = nullptr;
+
+const char* GetIntersectionMethodName(IntersectionMethod method) {
+ switch (method) {
+ case IntersectionMethod::SIMPLE: return "Simple";
+ case IntersectionMethod::SIMPLE_LD: return "Simple_ld";
+ case IntersectionMethod::STABLE: return "Stable";
+ case IntersectionMethod::STABLE_LD: return "Stable_ld";
+ case IntersectionMethod::EXACT: return "Exact";
+ default: return "Unknown";
+ }
+}
+
+} // namespace internal
+
+int CrossingSign(const S2Point& a, const S2Point& b,
+ const S2Point& c, const S2Point& d) {
+ S2EdgeCrosser crosser(&a, &b, &c);
+ return crosser.CrossingSign(&d);
+}
+
+bool VertexCrossing(const S2Point& a, const S2Point& b,
+ const S2Point& c, const S2Point& d) {
+ // If A == B or C == D there is no intersection. We need to check this
+ // case first in case 3 or more input points are identical.
+ if (a == b || c == d) return false;
+
+ // If any other pair of vertices is equal, there is a crossing if and only
+ // if OrderedCCW() indicates that the edge AB is further CCW around the
+ // shared vertex O (either A or B) than the edge CD, starting from an
+ // arbitrary fixed reference point.
+ //
+ // Optimization: if AB=CD or AB=DC, we can avoid most of the calculations.
+ if (a == c) return (b == d) || s2pred::OrderedCCW(S2::Ortho(a), d, b, a);
+ if (b == d) return s2pred::OrderedCCW(S2::Ortho(b), c, a, b);
+
+ if (a == d) return (b == c) || s2pred::OrderedCCW(S2::Ortho(a), c, b, a);
+ if (b == c) return s2pred::OrderedCCW(S2::Ortho(b), d, a, b);
+
+ S2_LOG(DFATAL) << "VertexCrossing called with 4 distinct vertices";
+ return false;
+}
+
+bool EdgeOrVertexCrossing(const S2Point& a, const S2Point& b,
+ const S2Point& c, const S2Point& d) {
+ int crossing = CrossingSign(a, b, c, d);
+ if (crossing < 0) return false;
+ if (crossing > 0) return true;
+ return VertexCrossing(a, b, c, d);
+}
+
+using Vector3_ld = Vector3<long double>;
+using Vector3_xf = Vector3<ExactFloat>;
+
+// Computes the cross product of "x" and "y", normalizes it to be unit length,
+// and stores the result in "result". Also returns the length of the cross
+// product before normalization, which is useful for estimating the amount of
+// error in the result. For numerical stability, "x" and "y" should both be
+// approximately unit length.
+template <class T>
+static T RobustNormalWithLength(const Vector3<T>& x, const Vector3<T>& y,
+ Vector3<T>* result) {
+ // This computes 2 * (x.CrossProd(y)), but has much better numerical
+ // stability when "x" and "y" are unit length.
+ Vector3<T> tmp = (x - y).CrossProd(x + y);
+ T length = tmp.Norm();
+ if (length != 0) {
+ *result = (1 / length) * tmp;
+ }
+ return 0.5 * length; // Since tmp == 2 * (x.CrossProd(y))
+}
+
+// If the intersection point of the edges (a0,a1) and (b0,b1) can be computed
+// to within an error of at most kIntersectionError by this function, then set
+// "result" to the intersection point and return true.
+//
+// The intersection point is not guaranteed to have the correct sign
+// (i.e., it may be either "result" or "-result").
+template <class T>
+static bool GetIntersectionSimple(const Vector3<T>& a0, const Vector3<T>& a1,
+ const Vector3<T>& b0, const Vector3<T>& b1,
+ Vector3<T>* result) {
+ // The code below computes the intersection point as
+ //
+ // (a0.CrossProd(a1)).CrossProd(b0.CrossProd(b1))
+ //
+ // except that it has better numerical stability and also computes a
+ // guaranteed error bound.
+ //
+ // Each cross product is computed as (X-Y).CrossProd(X+Y) using unit-length
+ // input vectors, which eliminates most of the cancellation error. However
+ // the error in the direction of the cross product can still become large if
+ // the two points are extremely close together. We can show that as long as
+ // the length of the cross product is at least (16 * sqrt(3) + 24) * DBL_ERR
+ // (about 6e-15), then the directional error is at most 5 * T_ERR (about
+ // 3e-19 when T == "long double"). (DBL_ERR appears in the first formula
+ // because the inputs are assumed to be normalized in double precision
+ // rather than in the given type T.)
+ //
+ // The third cross product is different because its inputs already have some
+ // error. Letting "result_len" be the length of the cross product, it can
+ // be shown that the error is at most
+ //
+ // (2 + 2 * sqrt(3) + 12 / result_len) * T_ERR
+ //
+ // We want this error to be at most kIntersectionError, which is true as
+ // long as "result_len" is at least kMinResultLen defined below.
+
+ constexpr T T_ERR = s2pred::rounding_epsilon<T>();
+ static const T kMinNormalLength = (16 * sqrt(3.0) + 24) * DBL_ERR;
+ static const T kMinResultLen =
+ 12 / (kIntersectionError.radians() / T_ERR - (2 + 2 * sqrt(3.0)));
+
+ // On some platforms "long double" is the same as "double", and on these
+ // platforms this method always returns false (e.g. ARM, Win32). Rather
+ // than testing this directly, instead we look at kMinResultLen since this
+ // is a direct measure of whether "long double" has sufficient accuracy to
+ // be useful. If kMinResultLen > 0.5, it means that this method will fail
+ // even for edges that meet at an angle of 30 degrees. (On Intel platforms
+ // kMinResultLen corresponds to an intersection angle of about 0.04
+ // degrees.)
+ S2_DCHECK_LE(kMinResultLen, 0.5);
+
+ Vector3<T> a_norm, b_norm;
+ if (RobustNormalWithLength(a0, a1, &a_norm) >= kMinNormalLength &&
+ RobustNormalWithLength(b0, b1, &b_norm) >= kMinNormalLength &&
+ RobustNormalWithLength(a_norm, b_norm, result) >= kMinResultLen) {
+ return true;
+ }
+ return false;
+}
+
+static bool GetIntersectionSimpleLD(const S2Point& a0, const S2Point& a1,
+ const S2Point& b0, const S2Point& b1,
+ S2Point* result) {
+ Vector3_ld result_ld;
+ if (GetIntersectionSimple(Vector3_ld::Cast(a0), Vector3_ld::Cast(a1),
+ Vector3_ld::Cast(b0), Vector3_ld::Cast(b1),
+ &result_ld)) {
+ *result = S2Point::Cast(result_ld);
+ return true;
+ }
+ return false;
+}
+
+// Given a point X and a vector "a_norm" (not necessarily unit length),
+// compute x.DotProd(a_norm) and return a bound on the error in the result.
+// The remaining parameters allow this dot product to be computed more
+// accurately and efficiently. They include the length of "a_norm"
+// ("a_norm_len") and the edge endpoints "a0" and "a1".
+template <class T>
+static T GetProjection(const Vector3<T>& x,
+ const Vector3<T>& a_norm, T a_norm_len,
+ const Vector3<T>& a0, const Vector3<T>& a1,
+ T* error) {
+ // The error in the dot product is proportional to the lengths of the input
+ // vectors, so rather than using "x" itself (a unit-length vector) we use
+ // the vectors from "x" to the closer of the two edge endpoints. This
+ // typically reduces the error by a huge factor.
+ Vector3<T> x0 = x - a0;
+ Vector3<T> x1 = x - a1;
+ T x0_dist2 = x0.Norm2();
+ T x1_dist2 = x1.Norm2();
+
+ // If both distances are the same, we need to be careful to choose one
+ // endpoint deterministically so that the result does not change if the
+ // order of the endpoints is reversed.
+ T dist, result;
+ if (x0_dist2 < x1_dist2 || (x0_dist2 == x1_dist2 && x0 < x1)) {
+ dist = sqrt(x0_dist2);
+ result = x0.DotProd(a_norm);
+ } else {
+ dist = sqrt(x1_dist2);
+ result = x1.DotProd(a_norm);
+ }
+ // This calculation bounds the error from all sources: the computation of
+ // the normal, the subtraction of one endpoint, and the dot product itself.
+ // (DBL_ERR appears because the input points are assumed to be normalized in
+ // double precision rather than in the given type T.)
+ //
+ // For reference, the bounds that went into this calculation are:
+ // ||N'-N|| <= ((1 + 2 * sqrt(3))||N|| + 32 * sqrt(3) * DBL_ERR) * T_ERR
+ // |(A.B)'-(A.B)| <= (1.5 * (A.B) + 1.5 * ||A|| * ||B||) * T_ERR
+ // ||(X-Y)'-(X-Y)|| <= ||X-Y|| * T_ERR
+ constexpr T T_ERR = s2pred::rounding_epsilon<T>();
+ *error = (((3.5 + 2 * sqrt(3.0)) * a_norm_len + 32 * sqrt(3.0) * DBL_ERR)
+ * dist + 1.5 * fabs(result)) * T_ERR;
+ return result;
+}
+
+// Helper function for GetIntersectionStable(). It expects that the edges
+// (a0,a1) and (b0,b1) have been sorted so that the first edge is longer.
+template <class T>
+static bool GetIntersectionStableSorted(
+ const Vector3<T>& a0, const Vector3<T>& a1,
+ const Vector3<T>& b0, const Vector3<T>& b1, Vector3<T>* result) {
+ S2_DCHECK_GE((a1 - a0).Norm2(), (b1 - b0).Norm2());
+
+ // Compute the normal of the plane through (a0, a1) in a stable way.
+ Vector3<T> a_norm = (a0 - a1).CrossProd(a0 + a1);
+ T a_norm_len = a_norm.Norm();
+ T b_len = (b1 - b0).Norm();
+
+ // Compute the projection (i.e., signed distance) of b0 and b1 onto the
+ // plane through (a0, a1). Distances are scaled by the length of a_norm.
+ T b0_error, b1_error;
+ T b0_dist = GetProjection(b0, a_norm, a_norm_len, a0, a1, &b0_error);
+ T b1_dist = GetProjection(b1, a_norm, a_norm_len, a0, a1, &b1_error);
+
+ // The total distance from b0 to b1 measured perpendicularly to (a0,a1) is
+ // |b0_dist - b1_dist|. Note that b0_dist and b1_dist generally have
+ // opposite signs because b0 and b1 are on opposite sides of (a0, a1). The
+ // code below finds the intersection point by interpolating along the edge
+ // (b0, b1) to a fractional distance of b0_dist / (b0_dist - b1_dist).
+ //
+ // It can be shown that the maximum error in the interpolation fraction is
+ //
+ // (b0_dist * b1_error - b1_dist * b0_error) /
+ // (dist_sum * (dist_sum - error_sum))
+ //
+ // We save ourselves some work by scaling the result and the error bound by
+ // "dist_sum", since the result is normalized to be unit length anyway.
+ T dist_sum = fabs(b0_dist - b1_dist);
+ T error_sum = b0_error + b1_error;
+ if (dist_sum <= error_sum) {
+ return false; // Error is unbounded in this case.
+ }
+ Vector3<T> x = b0_dist * b1 - b1_dist * b0;
+ constexpr T T_ERR = s2pred::rounding_epsilon<T>();
+ T error = b_len * fabs(b0_dist * b1_error - b1_dist * b0_error) /
+ (dist_sum - error_sum) + 2 * T_ERR * dist_sum;
+
+ // Finally we normalize the result, compute the corresponding error, and
+ // check whether the total error is acceptable.
+ T x_len2 = x.Norm2();
+ if (x_len2 < std::numeric_limits<T>::min()) {
+ // If x.Norm2() is less than the minimum normalized value of T, x_len might
+ // lose precision and the result might fail to satisfy S2::IsUnitLength().
+ // TODO(ericv): Implement S2::RobustNormalize().
+ return false;
+ }
+ T x_len = sqrt(x_len2);
+ const T kMaxError = kIntersectionError.radians();
+ if (error > (kMaxError - T_ERR) * x_len) {
+ return false;
+ }
+ *result = (1 / x_len) * x;
+ return true;
+}
+
+// Returns whether (a0,a1) is less than (b0,b1) with respect to a total
+// ordering on edges that is invariant under edge reversals.
+template <class T>
+static bool CompareEdges(const Vector3<T>& a0, const Vector3<T>& a1,
+ const Vector3<T>& b0, const Vector3<T>& b1) {
+ const Vector3<T> *pa0 = &a0, *pa1 = &a1;
+ const Vector3<T> *pb0 = &b0, *pb1 = &b1;
+ if (*pa0 >= *pa1) std::swap(pa0, pa1);
+ if (*pb0 >= *pb1) std::swap(pb0, pb1);
+ return *pa0 < *pb0 || (*pa0 == *pb0 && *pb0 < *pb1);
+}
+
+// If the intersection point of the edges (a0,a1) and (b0,b1) can be computed
+// to within an error of at most kIntersectionError by this function, then set
+// "result" to the intersection point and return true.
+//
+// The intersection point is not guaranteed to have the correct sign
+// (i.e., it may be either "result" or "-result").
+template <class T>
+static bool GetIntersectionStable(const Vector3<T>& a0, const Vector3<T>& a1,
+ const Vector3<T>& b0, const Vector3<T>& b1,
+ Vector3<T>* result) {
+ // Sort the two edges so that (a0,a1) is longer, breaking ties in a
+ // deterministic way that does not depend on the ordering of the endpoints.
+ // This is desirable for two reasons:
+ // - So that the result doesn't change when edges are swapped or reversed.
+ // - It reduces error, since the first edge is used to compute the edge
+ // normal (where a longer edge means less error), and the second edge
+ // is used for interpolation (where a shorter edge means less error).
+ T a_len2 = (a1 - a0).Norm2();
+ T b_len2 = (b1 - b0).Norm2();
+ if (a_len2 < b_len2 || (a_len2 == b_len2 && CompareEdges(a0, a1, b0, b1))) {
+ return GetIntersectionStableSorted(b0, b1, a0, a1, result);
+ } else {
+ return GetIntersectionStableSorted(a0, a1, b0, b1, result);
+ }
+}
+
+static bool GetIntersectionStableLD(const S2Point& a0, const S2Point& a1,
+ const S2Point& b0, const S2Point& b1,
+ S2Point* result) {
+ Vector3_ld result_ld;
+ if (GetIntersectionStable(Vector3_ld::Cast(a0), Vector3_ld::Cast(a1),
+ Vector3_ld::Cast(b0), Vector3_ld::Cast(b1),
+ &result_ld)) {
+ *result = S2Point::Cast(result_ld);
+ return true;
+ }
+ return false;
+}
+
+static S2Point S2PointFromExact(const Vector3_xf& xf) {
+ // If all components of "x" have absolute value less than about 1e-154,
+ // then x.Norm2() is zero in double precision due to underflow. Therefore
+ // we need to scale "x" by an appropriate power of 2 before the conversion.
+ S2Point x(xf[0].ToDouble(), xf[1].ToDouble(), xf[2].ToDouble());
+ if (x.Norm2() > 0) return x.Normalize();
+
+ // Scale so that the largest component magnitude is in the range [0.5, 1).
+ int exp = ExactFloat::kMinExp - 1;
+ for (int i = 0; i < 3; ++i) {
+ if (xf[i].is_normal()) exp = std::max(exp, xf[i].exp());
+ }
+ if (exp < ExactFloat::kMinExp) {
+ return S2Point(0, 0, 0);
+ }
+ return S2Point(ldexp(xf[0], -exp).ToDouble(),
+ ldexp(xf[1], -exp).ToDouble(),
+ ldexp(xf[2], -exp).ToDouble()).Normalize();
+}
+
+namespace internal {
+
+// Compute the intersection point of (a0, a1) and (b0, b1) using exact
+// arithmetic. Note that the result is not exact because it is rounded to
+// double precision. Also, the intersection point is not guaranteed to have
+// the correct sign (i.e., the return value may need to be negated).
+S2Point GetIntersectionExact(const S2Point& a0, const S2Point& a1,
+ const S2Point& b0, const S2Point& b1) {
+ // Since we are using exact arithmetic, we don't need to worry about
+ // numerical stability.
+ Vector3_xf a0_xf = Vector3_xf::Cast(a0);
+ Vector3_xf a1_xf = Vector3_xf::Cast(a1);
+ Vector3_xf b0_xf = Vector3_xf::Cast(b0);
+ Vector3_xf b1_xf = Vector3_xf::Cast(b1);
+ Vector3_xf a_norm_xf = a0_xf.CrossProd(a1_xf);
+ Vector3_xf b_norm_xf = b0_xf.CrossProd(b1_xf);
+ Vector3_xf x_xf = a_norm_xf.CrossProd(b_norm_xf);
+
+ // The final Normalize() call is done in double precision, which creates a
+ // directional error of up to 2 * DBL_ERR. (ToDouble() and Normalize() each
+ // contribute up to DBL_ERR of directional error.)
+ S2Point x = S2PointFromExact(x_xf);
+
+ if (x == S2Point(0, 0, 0)) {
+ // The two edges are exactly collinear, but we still consider them to be
+ // "crossing" because of simulation of simplicity. Out of the four
+ // endpoints, exactly two lie in the interior of the other edge. Of
+ // those two we return the one that is lexicographically smallest.
+ x = S2Point(10, 10, 10); // Greater than any valid S2Point
+ S2Point a_norm = S2PointFromExact(a_norm_xf);
+ S2Point b_norm = S2PointFromExact(b_norm_xf);
+ if (a_norm == S2Point(0, 0, 0) || b_norm == S2Point(0, 0, 0)) {
+ // TODO(ericv): To support antipodal edges properly, we would need to
+ // add an s2pred::CrossProd() function that computes the cross product
+ // using simulation of simplicity and rounds the result to the nearest
+ // floating-point representation.
+ S2_LOG(DFATAL) << "Exactly antipodal edges not supported by GetIntersection";
+ }
+ if (s2pred::OrderedCCW(b0, a0, b1, b_norm) && a0 < x) x = a0;
+ if (s2pred::OrderedCCW(b0, a1, b1, b_norm) && a1 < x) x = a1;
+ if (s2pred::OrderedCCW(a0, b0, a1, a_norm) && b0 < x) x = b0;
+ if (s2pred::OrderedCCW(a0, b1, a1, a_norm) && b1 < x) x = b1;
+ }
+ S2_DCHECK(S2::IsUnitLength(x));
+ return x;
+}
+
+} // namespace internal
+
+// Given three points "a", "x", "b", returns true if these three points occur
+// in the given order along the edge (a,b) to within the given tolerance.
+// More precisely, either "x" must be within "tolerance" of "a" or "b", or
+// when "x" is projected onto the great circle through "a" and "b" it must lie
+// along the edge (a,b) (i.e., the shortest path from "a" to "b").
+static bool ApproximatelyOrdered(const S2Point& a, const S2Point& x,
+ const S2Point& b, double tolerance) {
+ if ((x - a).Norm2() <= tolerance * tolerance) return true;
+ if ((x - b).Norm2() <= tolerance * tolerance) return true;
+ return s2pred::OrderedCCW(a, x, b, S2::RobustCrossProd(a, b).Normalize());
+}
+
+S2Point GetIntersection(const S2Point& a0, const S2Point& a1,
+ const S2Point& b0, const S2Point& b1) {
+ S2_DCHECK_GT(CrossingSign(a0, a1, b0, b1), 0);
+
+ // It is difficult to compute the intersection point of two edges accurately
+ // when the angle between the edges is very small. Previously we handled
+ // this by only guaranteeing that the returned intersection point is within
+ // kIntersectionError of each edge. However, this means that when the edges
+ // cross at a very small angle, the computed result may be very far from the
+ // true intersection point.
+ //
+ // Instead this function now guarantees that the result is always within
+ // kIntersectionError of the true intersection. This requires using more
+ // sophisticated techniques and in some cases extended precision.
+ //
+ // Three different techniques are implemented, but only two are used:
+ //
+ // - GetIntersectionSimple() computes the intersection point using
+ // numerically stable cross products in "long double" precision.
+ //
+ // - GetIntersectionStable() computes the intersection point using
+ // projection and interpolation, taking care to minimize cancellation
+ // error. This method exists in "double" and "long double" versions.
+ //
+ // - GetIntersectionExact() computes the intersection point using exact
+ // arithmetic and converts the final result back to an S2Point.
+ //
+ // We don't actually use the first method (GetIntersectionSimple) because it
+ // turns out that GetIntersectionStable() is twice as fast and also much
+ // more accurate (even in double precision). The "long double" version
+ // (only available on Intel platforms) uses 80-bit precision and is about
+ // twice as slow. The exact arithmetic version is about 100x slower.
+ //
+ // So our strategy is to first call GetIntersectionStable() in double
+ // precision; if that doesn't work and this platform supports "long double",
+ // then we try again in "long double"; if that doesn't work then we fall
+ // back to exact arithmetic.
+
+ static const bool kUseSimpleMethod = false;
+ static const bool kHasLongDouble = (s2pred::rounding_epsilon<long double>() <
+ s2pred::rounding_epsilon<double>());
+ S2Point result;
+ IntersectionMethod method;
+ if (kUseSimpleMethod && GetIntersectionSimple(a0, a1, b0, b1, &result)) {
+ method = IntersectionMethod::SIMPLE;
+ } else if (kUseSimpleMethod && kHasLongDouble &&
+ GetIntersectionSimpleLD(a0, a1, b0, b1, &result)) {
+ method = IntersectionMethod::SIMPLE_LD;
+ } else if (GetIntersectionStable(a0, a1, b0, b1, &result)) {
+ method = IntersectionMethod::STABLE;
+ } else if (kHasLongDouble &&
+ GetIntersectionStableLD(a0, a1, b0, b1, &result)) {
+ method = IntersectionMethod::STABLE_LD;
+ } else {
+ result = GetIntersectionExact(a0, a1, b0, b1);
+ method = IntersectionMethod::EXACT;
+ }
+ if (intersection_method_tally_) {
+ ++intersection_method_tally_[static_cast<int>(method)];
+ }
+
+ // Make sure the intersection point is on the correct side of the sphere.
+ // Since all vertices are unit length, and edges are less than 180 degrees,
+ // (a0 + a1) and (b0 + b1) both have positive dot product with the
+ // intersection point. We use the sum of all vertices to make sure that the
+ // result is unchanged when the edges are swapped or reversed.
+ if (result.DotProd((a0 + a1) + (b0 + b1)) < 0) result = -result;
+
+ // Make sure that the intersection point lies on both edges.
+ S2_DCHECK(ApproximatelyOrdered(a0, result, a1, kIntersectionError.radians()));
+ S2_DCHECK(ApproximatelyOrdered(b0, result, b1, kIntersectionError.radians()));
+
+ return result;
+}
+
+} // namespace S2
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2edge_distances.h"
+
+#include <cfloat>
+#include <cmath>
+
+#include "s2/base/logging.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s2edge_crossings.h"
+#include "s2/s2pointutil.h"
+#include "s2/s2predicates.h"
+
+using std::max;
+using std::min;
+
+namespace S2 {
+
+double GetDistanceFraction(const S2Point& x,
+ const S2Point& a0, const S2Point& a1) {
+ S2_DCHECK_NE(a0, a1);
+ double d0 = x.Angle(a0);
+ double d1 = x.Angle(a1);
+ return d0 / (d0 + d1);
+}
+
+S2Point InterpolateAtDistance(S1Angle ax_angle,
+ const S2Point& a, const S2Point& b) {
+ double ax = ax_angle.radians();
+
+ S2_DCHECK(S2::IsUnitLength(a));
+ S2_DCHECK(S2::IsUnitLength(b));
+
+ // Use RobustCrossProd() to compute the tangent vector at A towards B. The
+ // result is always perpendicular to A, even if A=B or A=-B, but it is not
+ // necessarily unit length. (We effectively normalize it below.)
+ Vector3_d normal = S2::RobustCrossProd(a, b);
+ Vector3_d tangent = normal.CrossProd(a);
+ S2_DCHECK(tangent != S2Point(0, 0, 0));
+
+ // Now compute the appropriate linear combination of A and "tangent". With
+ // infinite precision the result would always be unit length, but we
+ // normalize it anyway to ensure that the error is within acceptable bounds.
+ // (Otherwise errors can build up when the result of one interpolation is
+ // fed into another interpolation.)
+ return (cos(ax) * a + (sin(ax) / tangent.Norm()) * tangent).Normalize();
+}
+
+S2Point Interpolate(double t, const S2Point& a, const S2Point& b) {
+ if (t == 0) return a;
+ if (t == 1) return b;
+ S1Angle ab(a, b);
+ return InterpolateAtDistance(t * ab, a, b);
+}
+
+// If the minimum distance from X to AB is attained at an interior point of AB
+// (i.e., not an endpoint), and that distance is less than "min_dist" or
+// "always_update" is true, then update "min_dist" and return true. Otherwise
+// return false.
+//
+// The "Always" in the function name refers to the template argument, i.e.
+// AlwaysUpdateMinInteriorDistance<true> always updates the given distance,
+// while AlwaysUpdateMinInteriorDistance<false> does not. This optimization
+// increases the speed of GetDistance() by about 10% without creating code
+// duplication.
+template <bool always_update>
+inline bool AlwaysUpdateMinInteriorDistance(
+ const S2Point& x, const S2Point& a, const S2Point& b,
+ double xa2, double xb2, S1ChordAngle* min_dist) {
+ S2_DCHECK(S2::IsUnitLength(x) && S2::IsUnitLength(a) && S2::IsUnitLength(b));
+ S2_DCHECK_EQ(xa2, (x-a).Norm2());
+ S2_DCHECK_EQ(xb2, (x-b).Norm2());
+
+ // The closest point on AB could either be one of the two vertices (the
+ // "vertex case") or in the interior (the "interior case"). Let C = A x B.
+ // If X is in the spherical wedge extending from A to B around the axis
+ // through C, then we are in the interior case. Otherwise we are in the
+ // vertex case.
+ //
+ // Check whether we might be in the interior case. For this to be true, XAB
+ // and XBA must both be acute angles. Checking this condition exactly is
+ // expensive, so instead we consider the planar triangle ABX (which passes
+ // through the sphere's interior). The planar angles XAB and XBA are always
+ // less than the corresponding spherical angles, so if we are in the
+ // interior case then both of these angles must be acute.
+ //
+ // We check this by computing the squared edge lengths of the planar
+ // triangle ABX, and testing acuteness using the law of cosines:
+ //
+ // max(XA^2, XB^2) < min(XA^2, XB^2) + AB^2
+ //
+ if (max(xa2, xb2) >= min(xa2, xb2) + (a-b).Norm2()) {
+ return false;
+ }
+ // The minimum distance might be to a point on the edge interior. Let R
+ // be closest point to X that lies on the great circle through AB. Rather
+ // than computing the geodesic distance along the surface of the sphere,
+ // instead we compute the "chord length" through the sphere's interior.
+ // If the squared chord length exceeds min_dist.length2() then we can
+ // return "false" immediately.
+ //
+ // The squared chord length XR^2 can be expressed as XQ^2 + QR^2, where Q
+ // is the point X projected onto the plane through the great circle AB.
+ // The distance XQ^2 can be written as (X.C)^2 / |C|^2 where C = A x B.
+ // We ignore the QR^2 term and instead use XQ^2 as a lower bound, since it
+ // is faster and the corresponding distance on the Earth's surface is
+ // accurate to within 1% for distances up to about 1800km.
+ S2Point c = S2::RobustCrossProd(a, b);
+ double c2 = c.Norm2();
+ double x_dot_c = x.DotProd(c);
+ double x_dot_c2 = x_dot_c * x_dot_c;
+ if (!always_update && x_dot_c2 > c2 * min_dist->length2()) {
+ // The closest point on the great circle AB is too far away. We need to
+ // test this using ">" rather than ">=" because the actual minimum bound
+ // on the distance is (x_dot_c2 / c2), which can be rounded differently
+ // than the (more efficient) multiplicative test above.
+ return false;
+ }
+ // Otherwise we do the exact, more expensive test for the interior case.
+ // This test is very likely to succeed because of the conservative planar
+ // test we did initially.
+ S2Point cx = c.CrossProd(x);
+ if (a.DotProd(cx) >= 0 || b.DotProd(cx) <= 0) {
+ return false;
+ }
+ // Compute the squared chord length XR^2 = XQ^2 + QR^2 (see above).
+ // This calculation has good accuracy for all chord lengths since it
+ // is based on both the dot product and cross product (rather than
+ // deriving one from the other). However, note that the chord length
+ // representation itself loses accuracy as the angle approaches Pi.
+ double qr = 1 - sqrt(cx.Norm2() / c2);
+ double dist2 = (x_dot_c2 / c2) + (qr * qr);
+ if (!always_update && dist2 >= min_dist->length2()) {
+ return false;
+ }
+ *min_dist = S1ChordAngle::FromLength2(dist2);
+ return true;
+}
+
+// This function computes the distance from a point X to a line segment AB.
+// If the distance is less than "min_dist" or "always_update" is true, it
+// updates "min_dist" and returns true. Otherwise it returns false.
+//
+// The "Always" in the function name refers to the template argument, i.e.
+// AlwaysUpdateMinDistance<true> always updates the given distance, while
+// AlwaysUpdateMinDistance<false> does not. This optimization increases the
+// speed of GetDistance() by about 10% without creating code duplication.
+template <bool always_update>
+inline bool AlwaysUpdateMinDistance(const S2Point& x,
+ const S2Point& a, const S2Point& b,
+ S1ChordAngle* min_dist) {
+ S2_DCHECK(S2::IsUnitLength(x) && S2::IsUnitLength(a) && S2::IsUnitLength(b));
+
+ double xa2 = (x-a).Norm2(), xb2 = (x-b).Norm2();
+ if (AlwaysUpdateMinInteriorDistance<always_update>(x, a, b, xa2, xb2,
+ min_dist)) {
+ return true; // Minimum distance is attained along the edge interior.
+ }
+ // Otherwise the minimum distance is to one of the endpoints.
+ double dist2 = min(xa2, xb2);
+ if (!always_update && dist2 >= min_dist->length2()) {
+ return false;
+ }
+ *min_dist = S1ChordAngle::FromLength2(dist2);
+ return true;
+}
+
+S1Angle GetDistance(const S2Point& x, const S2Point& a, const S2Point& b) {
+ S1ChordAngle min_dist;
+ AlwaysUpdateMinDistance<true>(x, a, b, &min_dist);
+ return min_dist.ToAngle();
+}
+
+bool UpdateMinDistance(const S2Point& x, const S2Point& a, const S2Point& b,
+ S1ChordAngle* min_dist) {
+ return AlwaysUpdateMinDistance<false>(x, a, b, min_dist);
+}
+
+bool UpdateMaxDistance(const S2Point& x, const S2Point& a, const S2Point& b,
+ S1ChordAngle* max_dist) {
+ auto dist = max(S1ChordAngle(x, a), S1ChordAngle(x, b));
+ if (dist > S1ChordAngle::Right()) {
+ AlwaysUpdateMinDistance<true>(-x, a, b, &dist);
+ dist = S1ChordAngle::Straight() - dist;
+ }
+ if (*max_dist < dist) {
+ *max_dist = dist;
+ return true;
+ }
+
+ return false;
+}
+
+
+bool UpdateMinInteriorDistance(const S2Point& x,
+ const S2Point& a, const S2Point& b,
+ S1ChordAngle* min_dist) {
+ double xa2 = (x-a).Norm2(), xb2 = (x-b).Norm2();
+ return AlwaysUpdateMinInteriorDistance<false>(x, a, b, xa2, xb2, min_dist);
+}
+
+// Returns the maximum error in the result of UpdateMinInteriorDistance,
+// assuming that all input points are normalized to within the bounds
+// guaranteed by S2Point::Normalize(). The error can be added or subtracted
+// from an S1ChordAngle "x" using x.PlusError(error).
+static double GetUpdateMinInteriorDistanceMaxError(S1ChordAngle dist) {
+ // If a point is more than 90 degrees from an edge, then the minimum
+ // distance is always to one of the endpoints, not to the edge interior.
+ if (dist >= S1ChordAngle::Right()) return 0.0;
+
+ // This bound includes all source of error, assuming that the input points
+ // are normalized to within the bounds guaranteed to S2Point::Normalize().
+ // "a" and "b" are components of chord length that are perpendicular and
+ // parallel to the plane containing the edge respectively.
+ double b = min(1.0, 0.5 * dist.length2());
+ double a = sqrt(b * (2 - b));
+ return ((2.5 + 2 * sqrt(3.0) + 8.5 * a) * a +
+ (2 + 2 * sqrt(3.0) / 3 + 6.5 * (1 - b)) * b +
+ (23 + 16 / sqrt(3.0)) * DBL_EPSILON) * DBL_EPSILON;
+}
+
+double GetUpdateMinDistanceMaxError(S1ChordAngle dist) {
+ // There are two cases for the maximum error in UpdateMinDistance(),
+ // depending on whether the closest point is interior to the edge.
+ return max(GetUpdateMinInteriorDistanceMaxError(dist),
+ dist.GetS2PointConstructorMaxError());
+}
+
+S2Point Project(const S2Point& x, const S2Point& a, const S2Point& b,
+ const Vector3_d& a_cross_b) {
+ S2_DCHECK(S2::IsUnitLength(a));
+ S2_DCHECK(S2::IsUnitLength(b));
+ S2_DCHECK(S2::IsUnitLength(x));
+
+ // Find the closest point to X along the great circle through AB.
+ S2Point p = x - (x.DotProd(a_cross_b) / a_cross_b.Norm2()) * a_cross_b;
+
+ // If this point is on the edge AB, then it's the closest point.
+ if (S2::SimpleCCW(a_cross_b, a, p) && S2::SimpleCCW(p, b, a_cross_b)) {
+ return p.Normalize();
+ }
+ // Otherwise, the closest point is either A or B.
+ return ((x - a).Norm2() <= (x - b).Norm2()) ? a : b;
+}
+
+S2Point Project(const S2Point& x, const S2Point& a, const S2Point& b) {
+ return Project(x, a, b, S2::RobustCrossProd(a, b));
+}
+
+bool UpdateEdgePairMinDistance(
+ const S2Point& a0, const S2Point& a1,
+ const S2Point& b0, const S2Point& b1,
+ S1ChordAngle* min_dist) {
+ if (*min_dist == S1ChordAngle::Zero()) {
+ return false;
+ }
+ if (S2::CrossingSign(a0, a1, b0, b1) > 0) {
+ *min_dist = S1ChordAngle::Zero();
+ return true;
+ }
+ // Otherwise, the minimum distance is achieved at an endpoint of at least
+ // one of the two edges. We use "|" rather than "||" below to ensure that
+ // all four possibilities are always checked.
+ //
+ // The calculation below computes each of the six vertex-vertex distances
+ // twice (this could be optimized).
+ return (UpdateMinDistance(a0, b0, b1, min_dist) |
+ UpdateMinDistance(a1, b0, b1, min_dist) |
+ UpdateMinDistance(b0, a0, a1, min_dist) |
+ UpdateMinDistance(b1, a0, a1, min_dist));
+}
+
+bool UpdateEdgePairMaxDistance(
+ const S2Point& a0, const S2Point& a1,
+ const S2Point& b0, const S2Point& b1,
+ S1ChordAngle* max_dist) {
+ if (*max_dist == S1ChordAngle::Straight()) {
+ return false;
+ }
+ if (S2::CrossingSign(a0, a1, -b0, -b1) > 0) {
+ *max_dist = S1ChordAngle::Straight();
+ return true;
+ }
+ // Otherwise, the maximum distance is achieved at an endpoint of at least
+ // one of the two edges. We use "|" rather than "||" below to ensure that
+ // all four possibilities are always checked.
+ //
+ // The calculation below computes each of the six vertex-vertex distances
+ // twice (this could be optimized).
+ return (UpdateMaxDistance(a0, b0, b1, max_dist) |
+ UpdateMaxDistance(a1, b0, b1, max_dist) |
+ UpdateMaxDistance(b0, a0, a1, max_dist) |
+ UpdateMaxDistance(b1, a0, a1, max_dist));
+}
+
+std::pair<S2Point, S2Point> GetEdgePairClosestPoints(
+ const S2Point& a0, const S2Point& a1,
+ const S2Point& b0, const S2Point& b1) {
+ if (S2::CrossingSign(a0, a1, b0, b1) > 0) {
+ S2Point x = S2::GetIntersection(a0, a1, b0, b1);
+ return std::make_pair(x, x);
+ }
+ // We save some work by first determining which vertex/edge pair achieves
+ // the minimum distance, and then computing the closest point on that edge.
+ S1ChordAngle min_dist;
+ AlwaysUpdateMinDistance<true>(a0, b0, b1, &min_dist);
+ enum { A0, A1, B0, B1 } closest_vertex = A0;
+ if (UpdateMinDistance(a1, b0, b1, &min_dist)) { closest_vertex = A1; }
+ if (UpdateMinDistance(b0, a0, a1, &min_dist)) { closest_vertex = B0; }
+ if (UpdateMinDistance(b1, a0, a1, &min_dist)) { closest_vertex = B1; }
+ switch (closest_vertex) {
+ case A0: return std::make_pair(a0, Project(a0, b0, b1));
+ case A1: return std::make_pair(a1, Project(a1, b0, b1));
+ case B0: return std::make_pair(Project(b0, a0, a1), b0);
+ case B1: return std::make_pair(Project(b1, a0, a1), b1);
+ default: S2_LOG(FATAL) << "Unreached (to suppress Android compiler warning)";
+ }
+}
+
+bool IsEdgeBNearEdgeA(const S2Point& a0, const S2Point& a1,
+ const S2Point& b0, const S2Point& b1,
+ S1Angle tolerance) {
+ S2_DCHECK_LT(tolerance.radians(), M_PI / 2);
+ S2_DCHECK_GT(tolerance.radians(), 0);
+ // The point on edge B=b0b1 furthest from edge A=a0a1 is either b0, b1, or
+ // some interior point on B. If it is an interior point on B, then it must be
+ // one of the two points where the great circle containing B (circ(B)) is
+ // furthest from the great circle containing A (circ(A)). At these points,
+ // the distance between circ(B) and circ(A) is the angle between the planes
+ // containing them.
+
+ Vector3_d a_ortho = S2::RobustCrossProd(a0, a1).Normalize();
+ const S2Point a_nearest_b0 = Project(b0, a0, a1, a_ortho);
+ const S2Point a_nearest_b1 = Project(b1, a0, a1, a_ortho);
+ // If a_nearest_b0 and a_nearest_b1 have opposite orientation from a0 and a1,
+ // we invert a_ortho so that it points in the same direction as a_nearest_b0 x
+ // a_nearest_b1. This helps us handle the case where A and B are oppositely
+ // oriented but otherwise might be near each other. We check orientation and
+ // invert rather than computing a_nearest_b0 x a_nearest_b1 because those two
+ // points might be equal, and have an unhelpful cross product.
+ if (s2pred::Sign(a_ortho, a_nearest_b0, a_nearest_b1) < 0) a_ortho *= -1;
+
+ // To check if all points on B are within tolerance of A, we first check to
+ // see if the endpoints of B are near A. If they are not, B is not near A.
+ const S1Angle b0_distance(b0, a_nearest_b0);
+ const S1Angle b1_distance(b1, a_nearest_b1);
+ if (b0_distance > tolerance || b1_distance > tolerance)
+ return false;
+
+ // If b0 and b1 are both within tolerance of A, we check to see if the angle
+ // between the planes containing B and A is greater than tolerance. If it is
+ // not, no point on B can be further than tolerance from A (recall that we
+ // already know that b0 and b1 are close to A, and S2Edges are all shorter
+ // than 180 degrees). The angle between the planes containing circ(A) and
+ // circ(B) is the angle between their normal vectors.
+ const Vector3_d b_ortho = S2::RobustCrossProd(b0, b1).Normalize();
+ const S1Angle planar_angle(a_ortho, b_ortho);
+ if (planar_angle <= tolerance)
+ return true;
+
+
+ // As planar_angle approaches M_PI, the projection of a_ortho onto the plane
+ // of B approaches the null vector, and normalizing it is numerically
+ // unstable. This makes it unreliable or impossible to identify pairs of
+ // points where circ(A) is furthest from circ(B). At this point in the
+ // algorithm, this can only occur for two reasons:
+ //
+ // 1.) b0 and b1 are closest to A at distinct endpoints of A, in which case
+ // the opposite orientation of a_ortho and b_ortho means that A and B are
+ // in opposite hemispheres and hence not close to each other.
+ //
+ // 2.) b0 and b1 are closest to A at the same endpoint of A, in which case
+ // the orientation of a_ortho was chosen arbitrarily to be that of a0
+ // cross a1. B must be shorter than 2*tolerance and all points in B are
+ // close to one endpoint of A, and hence to A.
+ //
+ // The logic applies when planar_angle is robustly greater than M_PI/2, but
+ // may be more computationally expensive than the logic beyond, so we choose a
+ // value close to M_PI.
+ if (planar_angle >= S1Angle::Radians(M_PI - 0.01)) {
+ return (S1Angle(b0, a0) < S1Angle(b0, a1)) ==
+ (S1Angle(b1, a0) < S1Angle(b1, a1));
+ }
+
+ // Finally, if either of the two points on circ(B) where circ(B) is furthest
+ // from circ(A) lie on edge B, edge B is not near edge A.
+ //
+ // The normalized projection of a_ortho onto the plane of circ(B) is one of
+ // the two points along circ(B) where it is furthest from circ(A). The other
+ // is -1 times the normalized projection.
+ S2Point furthest = (a_ortho - a_ortho.DotProd(b_ortho) * b_ortho).Normalize();
+ S2_DCHECK(S2::IsUnitLength(furthest));
+ S2Point furthest_inv = -1 * furthest;
+
+ // A point p lies on B if you can proceed from b_ortho to b0 to p to b1 and
+ // back to b_ortho without ever turning right. We test this for furthest and
+ // furthest_inv, and return true if neither point lies on B.
+ return !((s2pred::Sign(b_ortho, b0, furthest) > 0 &&
+ s2pred::Sign(furthest, b1, b_ortho) > 0) ||
+ (s2pred::Sign(b_ortho, b0, furthest_inv) > 0 &&
+ s2pred::Sign(furthest_inv, b1, b_ortho) > 0));
+}
+
+} // namespace S2
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2edge_tessellator.h"
+
+#include <cmath>
+#include "s2/s2edge_distances.h"
+#include "s2/s2latlng.h"
+#include "s2/s2pointutil.h"
+
+using std::vector;
+// Tessellation is implemented by subdividing the edge until the estimated
+// maximum error is below the given tolerance. Estimating error is a hard
+// problem, especially when the only methods available are point evaluation of
+// the projection and its inverse. (These are the only methods that
+// S2::Projection provides, which makes it easier and less error-prone to
+// implement new projections.)
+//
+// One technique that significantly increases robustness is to treat the
+// geodesic and projected edges as parametric curves rather than geometric ones.
+// Given a spherical edge AB and a projection p:S2->R2, let f(t) be the
+// normalized arc length parametrization of AB and let g(t) be the normalized
+// arc length parameterization of the projected edge p(A)p(B). (In other words,
+// f(0)=A, f(1)=B, g(0)=p(A), g(1)=p(B).) We now define the geometric error as
+// the maximum distance from the point p^-1(g(t)) to the geodesic edge AB for
+// any t in [0,1], where p^-1 denotes the inverse projection. In other words,
+// the geometric error is the maximum distance from any point on the projected
+// edge (mapped back onto the sphere) to the geodesic edge AB. On the other
+// hand we define the parametric error as the maximum distance between the
+// points f(t) and p^-1(g(t)) for any t in [0,1], i.e. the maximum distance
+// (measured on the sphere) between the geodesic and projected points at the
+// same interpolation fraction t.
+//
+// The easiest way to estimate the parametric error is to simply evaluate both
+// edges at their midpoints and measure the distance between them (the "midpoint
+// method"). This is very fast and works quite well for most edges, however it
+// has one major drawback: it doesn't handle points of inflection (i.e., points
+// where the curvature changes sign). For example, edges in the Mercator and
+// Plate Carree projections always curve towards the equator relative to the
+// corresponding geodesic edge, so in these projections there is a point of
+// inflection whenever the projected edge crosses the equator. The worst case
+// occurs when the edge endpoints have different longitudes but the same
+// absolute latitude, since in that case the error is non-zero but the edges
+// have exactly the same midpoint (on the equator).
+//
+// One solution to this problem is to split the input edges at all inflection
+// points (i.e., along the equator in the case of the Mercator and Plate Carree
+// projections). However for general projections these inflection points can
+// occur anywhere on the sphere (e.g., consider the Transverse Mercator
+// projection). This could be addressed by adding methods to the S2Projection
+// interface to split edges at inflection points but this would make it harder
+// and more error-prone to implement new projections.
+//
+// Another problem with this approach is that the midpoint method sometimes
+// underestimates the true error even when edges do not cross the equator. For
+// the Plate Carree and Mercator projections, the midpoint method can
+// underestimate the error by up to 3%.
+//
+// Both of these problems can be solved as follows. We assume that the error
+// can be modeled as a convex combination of two worst-case functions, one
+// where the error is maximized at the edge midpoint and another where the
+// error is *minimized* (i.e., zero) at the edge midpoint. For example, we
+// could choose these functions as:
+//
+// E1(x) = 1 - x^2
+// E2(x) = x * (1 - x^2)
+//
+// where for convenience we use an interpolation parameter "x" in the range
+// [-1, 1] rather than the original "t" in the range [0, 1]. Note that both
+// error functions must have roots at x = {-1, 1} since the error must be zero
+// at the edge endpoints. E1 is simply a parabola whose maximum value is 1
+// attained at x = 0, while E2 is a cubic with an additional root at x = 0,
+// and whose maximum value is 2 * sqrt(3) / 9 attained at x = 1 / sqrt(3).
+//
+// Next, it is convenient to scale these functions so that the both have a
+// maximum value of 1. E1 already satisfies this requirement, and we simply
+// redefine E2 as
+//
+// E2(x) = x * (1 - x^2) / (2 * sqrt(3) / 9)
+//
+// Now define x0 to be the point where these two functions intersect, i.e. the
+// point in the range (-1, 1) where E1(x0) = E2(x0). This value has the very
+// convenient property that if we evaluate the actual error E(x0), then the
+// maximum error on the entire interval [-1, 1] is bounded by
+//
+// E(x) <= E(x0) / E1(x0)
+//
+// since whether the error is modeled using E1 or E2, the resulting function
+// has the same maximum value (namely E(x0) / E1(x0)). If it is modeled as
+// some other convex combination of E1 and E2, the maximum value can only
+// decrease.
+//
+// Finally, since E2 is not symmetric about the y-axis, we must also allow for
+// the possibility that the error is a convex combination of E1 and -E2. This
+// can be handled by evaluating the error at E(-x0) as well, and then
+// computing the final error bound as
+//
+// E(x) <= max(E(x0), E(-x0)) / E1(x0) .
+//
+// Effectively, this method is simply evaluating the error at two points about
+// 1/3 and 2/3 of the way along the edges, and then scaling the maximum of
+// these two errors by a constant factor. Intuitively, the reason this works
+// is that if the two edges cross somewhere in the interior, then at least one
+// of these points will be far from the crossing.
+//
+// The actual algorithm implemented below has some additional refinements.
+// First, edges longer than 90 degrees are always subdivided; this avoids
+// various unusual situations that can happen with very long edges, and there
+// is really no reason to avoid adding vertices to edges that are so long.
+//
+// Second, the error function E1 above needs to be modified to take into
+// account spherical distortions. (It turns out that spherical distortions are
+// beneficial in the case of E2, i.e. they only make its error estimates
+// slightly more conservative.) To do this, we model E1 as the maximum error
+// in a Plate Carree edge of length 90 degrees or less. This turns out to be
+// an edge from 45:-90 to 45:90 (in lat:lng format). The corresponding error
+// as a function of "x" in the range [-1, 1] can be computed as the distance
+// between the the the Plate Caree edge point (45, 90 * x) and the geodesic
+// edge point (90 - 45 * abs(x), 90 * sgn(x)). Using the Haversine formula,
+// the corresponding function E1 (normalized to have a maximum value of 1) is:
+//
+// E1(x) =
+// asin(sqrt(sin(Pi / 8 * (1 - x)) ^ 2 +
+// sin(Pi / 4 * (1 - x)) ^ 2 * cos(Pi / 4) * sin(Pi / 4 * x))) /
+// asin(sqrt((1 - 1 / sqrt(2)) / 2))
+//
+// Note that this function does not need to be evaluated at runtime, it
+// simply affects the calculation of the value x0 where E1(x0) = E2(x0)
+// and the corresponding scaling factor C = 1 / E1(x0).
+//
+// ------------------------------------------------------------------
+//
+// In the case of the Mercator and Plate Carree projections this strategy
+// produces a conservative upper bound (verified using 10 million random
+// edges). Furthermore the bound is nearly tight; the scaling constant is
+// C = 1.19289, whereas the maximum observed value was 1.19254.
+//
+// Compared to the simpler midpoint evaluation method, this strategy requires
+// more function evaluations (currently twice as many, but with a smarter
+// tessellation algorithm it will only be 50% more). It also results in a
+// small amount of additional tessellation (about 1.5%) compared to the
+// midpoint method, but this is due almost entirely to the fact that the
+// midpoint method does not yield conservative error estimates.
+//
+// For random edges with a tolerance of 1 meter, the expected amount of
+// overtessellation is as follows:
+//
+// Midpoint Method Cubic Method
+// Plate Carree 1.8% 3.0%
+// Mercator 15.8% 17.4%
+
+// The interpolation fraction at which the two edges are evaluated in order to
+// measure the error between them. (Edges are evaluated at two points measured
+// this fraction from either end.) With respect to the algorithm description
+// above, this value is t0 = (1 - x0) / 2 in the range [0, 1] that corresponds
+// to x0 in the range [-1, 1] chosen such that E1(x0) == E2(x0).
+static constexpr double kInterpolationFraction = 0.31215691082248312;
+
+// The following is the value of E1(x0) == E2(x0).
+static constexpr double kScaleFactor = 0.83829992569888509;
+
+S1Angle S2EdgeTessellator::kMinTolerance() {
+ // This distance is less than 1 micrometer on the Earth's surface, but is
+ // still much larger than the expected projection and interpolation errors.
+ return S1Angle::Radians(1e-13);
+}
+
+S2EdgeTessellator::S2EdgeTessellator(const S2::Projection* projection,
+ S1Angle tolerance)
+ : proj_(*projection) {
+ if (tolerance < kMinTolerance()) S2_LOG(DFATAL) << "Tolerance too small";
+
+ // Rather than scaling the error estimate as described above, instead we scale
+ // the tolerance. See algorithm description at the top of this file.
+ scaled_tolerance_ = S1ChordAngle(kScaleFactor *
+ std::max(tolerance, kMinTolerance()));
+}
+
+S1ChordAngle S2EdgeTessellator::EstimateMaxError(
+ const R2Point& pa, const S2Point& a, const R2Point& pb, const S2Point& b)
+ const {
+ // See the algorithm description at the top of this file.
+
+ // We always tessellate edges longer than 90 degrees on the sphere, since the
+ // approximation below is not robust enough to handle such edges.
+ if (a.DotProd(b) < -1e-14) return S1ChordAngle::Infinity();
+
+ constexpr double t1 = kInterpolationFraction;
+ constexpr double t2 = 1 - kInterpolationFraction;
+ S2Point mid1 = S2::Interpolate(t1, a, b);
+ S2Point mid2 = S2::Interpolate(t2, a, b);
+ S2Point pmid1 = proj_.Unproject(proj_.Interpolate(t1, pa, pb));
+ S2Point pmid2 = proj_.Unproject(proj_.Interpolate(t2, pa, pb));
+ return std::max(S1ChordAngle(mid1, pmid1), S1ChordAngle(mid2, pmid2));
+}
+
+void S2EdgeTessellator::AppendProjected(
+ const S2Point& a, const S2Point& b, vector<R2Point>* vertices) const {
+ R2Point pa = proj_.Project(a);
+ if (vertices->empty()) {
+ vertices->push_back(pa);
+ } else {
+ pa = proj_.WrapDestination(vertices->back(), pa);
+ S2_DCHECK_EQ(vertices->back(), pa) << "Appended edges must form a chain";
+ }
+ R2Point pb = proj_.Project(b);
+ AppendProjected(pa, a, pb, b, vertices);
+}
+
+// Given a geodesic edge AB, split the edge as necessary and append all
+// projected vertices except the first to "vertices".
+//
+// The maximum recursion depth is (M_PI / kMinTolerance()) < 45, and the
+// frame size is small so stack overflow should not be an issue.
+void S2EdgeTessellator::AppendProjected(const R2Point& pa, const S2Point& a,
+ const R2Point& pb_in, const S2Point& b,
+ vector<R2Point>* vertices) const {
+ R2Point pb = proj_.WrapDestination(pa, pb_in);
+ if (EstimateMaxError(pa, a, pb, b) <= scaled_tolerance_) {
+ vertices->push_back(pb);
+ } else {
+ S2Point mid = (a + b).Normalize();
+ R2Point pmid = proj_.WrapDestination(pa, proj_.Project(mid));
+ AppendProjected(pa, a, pmid, mid, vertices);
+ AppendProjected(pmid, mid, pb, b, vertices);
+ }
+}
+
+void S2EdgeTessellator::AppendUnprojected(
+ const R2Point& pa, const R2Point& pb, vector<S2Point>* vertices) const {
+ S2Point a = proj_.Unproject(pa);
+ S2Point b = proj_.Unproject(pb);
+ if (vertices->empty()) {
+ vertices->push_back(a);
+ } else {
+ // Note that coordinate wrapping can create a small amount of error. For
+ // example in the edge chain "0:-175, 0:179, 0:-177", the first edge is
+ // transformed into "0:-175, 0:-181" while the second is transformed into
+ // "0:179, 0:183". The two coordinate pairs for the middle vertex
+ // ("0:-181" and "0:179") may not yield exactly the same S2Point.
+ S2_DCHECK(S2::ApproxEquals(vertices->back(), a))
+ << "Appended edges must form a chain";
+ }
+ AppendUnprojected(pa, a, pb, b, vertices);
+}
+
+// Like AppendProjected, but interpolates a projected edge and appends the
+// corresponding points on the sphere.
+void S2EdgeTessellator::AppendUnprojected(
+ const R2Point& pa, const S2Point& a,
+ const R2Point& pb_in, const S2Point& b, vector<S2Point>* vertices) const {
+ // See notes above regarding measuring the interpolation error.
+ R2Point pb = proj_.WrapDestination(pa, pb_in);
+ if (EstimateMaxError(pa, a, pb, b) <= scaled_tolerance_) {
+ vertices->push_back(b);
+ } else {
+ R2Point pmid = proj_.Interpolate(0.5, pa, pb);
+ S2Point mid = proj_.Unproject(pmid);
+ AppendUnprojected(pa, a, pmid, mid, vertices);
+ AppendUnprojected(pmid, mid, pb, b, vertices);
+ }
+}
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2error.h"
+
+#include "s2/base/stringprintf.h"
+
+void S2Error::Init(Code code, const char* format, ...) {
+ code_ = code;
+ text_.clear();
+ va_list ap;
+ va_start(ap, format);
+ StringAppendV(&text_, format, ap);
+ va_end(ap);
+}
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "s2/s2furthest_edge_query.h"
+
+#include <vector>
+
+#include "s2/s2edge_distances.h"
+
+void S2FurthestEdgeQuery::Options::set_conservative_min_distance(
+ S1ChordAngle min_distance) {
+ set_max_distance(Distance(min_distance.PlusError(
+ -S2::GetUpdateMinDistanceMaxError(min_distance)).Predecessor()));
+}
+
+void S2FurthestEdgeQuery::Options::set_conservative_min_distance(
+ S1Angle min_distance) {
+ set_conservative_min_distance(S1ChordAngle(min_distance));
+}
+
+// See s2closest_edge_query.cc for justifications of
+// max_brute_force_index_size() for that query.
+int S2FurthestEdgeQuery::PointTarget::max_brute_force_index_size() const {
+ // Using BM_FindFurthest (which finds the single furthest edge), the
+ // break-even points are approximately 100, 400, and 600 edges for point
+ // cloud, fractal, and regular loop geometry respectively.
+ return 300;
+}
+
+int S2FurthestEdgeQuery::EdgeTarget::max_brute_force_index_size() const {
+ // Using BM_FindFurthestToEdge (which finds the single furthest edge), the
+ // break-even points are approximately 80, 100, and 230 edges for point
+ // cloud, fractal, and regular loop geometry respectively.
+ return 110;
+}
+
+int S2FurthestEdgeQuery::CellTarget::max_brute_force_index_size() const {
+ // Using BM_FindFurthestToCell (which finds the single furthest edge), the
+ // break-even points are approximately 70, 100, and 170 edges for point
+ // cloud, fractal, and regular loop geometry respectively.
+ return 100;
+}
+
+int S2FurthestEdgeQuery::ShapeIndexTarget::max_brute_force_index_size() const {
+ // For BM_FindFurthestToSameSizeAbuttingIndex (which uses two nearby indexes
+ // with similar edge counts), the break-even points are approximately 30,
+ // 100, and 130 edges for point cloud, fractal, and regular loop geometry
+ // respectively.
+ return 70;
+}
+
+S2FurthestEdgeQuery::S2FurthestEdgeQuery() {
+ // Prevent inline constructor bloat by defining here.
+}
+
+S2FurthestEdgeQuery::~S2FurthestEdgeQuery() {
+ // Prevent inline destructor bloat by defining here.
+}
+
+void S2FurthestEdgeQuery::FindFurthestEdges(
+ Target* target, std::vector<S2FurthestEdgeQuery::Result>* results) {
+ results->clear();
+ for (auto result : base_.FindClosestEdges(target, options_)) {
+ results->push_back(S2FurthestEdgeQuery::Result(result));
+ }
+}
+
+S2FurthestEdgeQuery::Result S2FurthestEdgeQuery::FindFurthestEdge(
+ Target* target) {
+ static_assert(sizeof(Options) <= 32, "Consider not copying Options here");
+ Options tmp_options = options_;
+ tmp_options.set_max_results(1);
+ Base::Result base_result = base_.FindClosestEdge(target, tmp_options);
+ return S2FurthestEdgeQuery::Result(base_result);
+}
+
+bool S2FurthestEdgeQuery::IsDistanceGreater(
+ Target* target, S1ChordAngle limit) {
+ static_assert(sizeof(Options) <= 32, "Consider not copying Options here");
+ Options tmp_options = options_;
+ tmp_options.set_max_results(1);
+ tmp_options.set_min_distance(limit);
+ tmp_options.set_max_error(S1ChordAngle::Straight());
+ return base_.FindClosestEdge(target, tmp_options).shape_id() >= 0;
+}
+
+bool S2FurthestEdgeQuery::IsDistanceGreaterOrEqual(
+ Target* target, S1ChordAngle limit) {
+ static_assert(sizeof(Options) <= 32, "Consider not copying Options here");
+ Options tmp_options = options_;
+ tmp_options.set_max_results(1);
+ tmp_options.set_inclusive_min_distance(limit);
+ tmp_options.set_max_error(S1ChordAngle::Straight());
+ return base_.FindClosestEdge(target, tmp_options).shape_id() >= 0;
+}
+
+bool S2FurthestEdgeQuery::IsConservativeDistanceGreaterOrEqual(
+ Target* target, S1ChordAngle limit) {
+ static_assert(sizeof(Options) <= 32, "Consider not copying Options here");
+ Options tmp_options = options_;
+ tmp_options.set_max_results(1);
+ tmp_options.set_conservative_min_distance(limit);
+ tmp_options.set_max_error(S1ChordAngle::Straight());
+ return base_.FindClosestEdge(target, tmp_options).shape_id() >= 0;
+}
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2latlng.h"
+
+#include <algorithm>
+#include <ostream>
+
+#include "s2/base/logging.h"
+#include "s2/base/stringprintf.h"
+
+using std::max;
+using std::min;
+
+S2LatLng S2LatLng::Normalized() const {
+ // remainder(x, 2 * M_PI) reduces its argument to the range [-M_PI, M_PI]
+ // inclusive, which is what we want here.
+ return S2LatLng(max(-M_PI_2, min(M_PI_2, lat().radians())),
+ remainder(lng().radians(), 2 * M_PI));
+}
+
+S2Point S2LatLng::ToPoint() const {
+ S2_DLOG_IF(ERROR, !is_valid())
+ << "Invalid S2LatLng in S2LatLng::ToPoint: " << *this;
+ double phi = lat().radians();
+ double theta = lng().radians();
+ double cosphi = cos(phi);
+ return S2Point(cos(theta) * cosphi, sin(theta) * cosphi, sin(phi));
+}
+
+S2LatLng::S2LatLng(const S2Point& p)
+ : coords_(Latitude(p).radians(), Longitude(p).radians()) {
+ // The latitude and longitude are already normalized.
+ S2_DLOG_IF(ERROR, !is_valid())
+ << "Invalid S2LatLng in constructor: " << *this;
+}
+
+S1Angle S2LatLng::GetDistance(const S2LatLng& o) const {
+ // This implements the Haversine formula, which is numerically stable for
+ // small distances but only gets about 8 digits of precision for very large
+ // distances (e.g. antipodal points). Note that 8 digits is still accurate
+ // to within about 10cm for a sphere the size of the Earth.
+ //
+ // This could be fixed with another sin() and cos() below, but at that point
+ // you might as well just convert both arguments to S2Points and compute the
+ // distance that way (which gives about 15 digits of accuracy for all
+ // distances).
+
+ S2_DLOG_IF(ERROR, !is_valid())
+ << "Invalid S2LatLng in S2LatLng::GetDistance: " << *this;
+
+ S2_DLOG_IF(ERROR, !o.is_valid())
+ << "Invalid S2LatLng in S2LatLng::GetDistance: " << o;
+
+ double lat1 = lat().radians();
+ double lat2 = o.lat().radians();
+ double lng1 = lng().radians();
+ double lng2 = o.lng().radians();
+ double dlat = sin(0.5 * (lat2 - lat1));
+ double dlng = sin(0.5 * (lng2 - lng1));
+ double x = dlat * dlat + dlng * dlng * cos(lat1) * cos(lat2);
+ return S1Angle::Radians(2 * asin(sqrt(min(1.0, x))));
+}
+
+string S2LatLng::ToStringInDegrees() const {
+ S2LatLng pt = Normalized();
+ return StringPrintf("%f,%f", pt.lat().degrees(), pt.lng().degrees());
+}
+
+void S2LatLng::ToStringInDegrees(string* s) const {
+ *s = ToStringInDegrees();
+}
+
+std::ostream& operator<<(std::ostream& os, const S2LatLng& ll) {
+ return os << "[" << ll.lat() << ", " << ll.lng() << "]";
+}
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2latlng_rect.h"
+
+#include <algorithm>
+#include <cmath>
+#include <iosfwd>
+#include <iostream>
+
+#include "s2/base/logging.h"
+#include "s2/util/coding/coder.h"
+#include "s2/s2cap.h"
+#include "s2/s2cell.h"
+#include "s2/s2debug.h"
+#include "s2/s2edge_crossings.h"
+#include "s2/s2edge_distances.h"
+#include "s2/s2pointutil.h"
+
+using std::fabs;
+using std::max;
+using std::min;
+
+static const unsigned char kCurrentLosslessEncodingVersionNumber = 1;
+
+S2LatLngRect S2LatLngRect::FromCenterSize(const S2LatLng& center,
+ const S2LatLng& size) {
+ return FromPoint(center).Expanded(0.5 * size);
+}
+
+S2LatLngRect S2LatLngRect::FromPoint(const S2LatLng& p) {
+ S2_DLOG_IF(ERROR, !p.is_valid())
+ << "Invalid S2LatLng in S2LatLngRect::GetDistance: " << p;
+
+ return S2LatLngRect(p, p);
+}
+
+S2LatLngRect S2LatLngRect::FromPointPair(const S2LatLng& p1,
+ const S2LatLng& p2) {
+ S2_DLOG_IF(ERROR, !p1.is_valid())
+ << "Invalid S2LatLng in S2LatLngRect::FromPointPair: " << p1;
+
+ S2_DLOG_IF(ERROR, !p2.is_valid())
+ << "Invalid S2LatLng in S2LatLngRect::FromPointPair: " << p2;
+
+ return S2LatLngRect(R1Interval::FromPointPair(p1.lat().radians(),
+ p2.lat().radians()),
+ S1Interval::FromPointPair(p1.lng().radians(),
+ p2.lng().radians()));
+}
+
+S2LatLngRect* S2LatLngRect::Clone() const {
+ return new S2LatLngRect(*this);
+}
+
+S2LatLng S2LatLngRect::GetVertex(int k) const {
+ // Twiddle bits to return the points in CCW order (lower left, lower right,
+ // upper right, upper left).
+ int i = (k >> 1) & 1;
+ return S2LatLng::FromRadians(lat_[i], lng_[i ^ (k & 1)]);
+}
+
+S2LatLng S2LatLngRect::GetCenter() const {
+ return S2LatLng::FromRadians(lat_.GetCenter(), lng_.GetCenter());
+}
+
+S2LatLng S2LatLngRect::GetSize() const {
+ return S2LatLng::FromRadians(lat_.GetLength(), lng_.GetLength());
+}
+
+double S2LatLngRect::Area() const {
+ if (is_empty()) return 0.0;
+ // This is the size difference of the two spherical caps, multiplied by
+ // the longitude ratio.
+ return lng().GetLength() * (sin(lat_hi()) - sin(lat_lo()));
+}
+
+S2Point S2LatLngRect::GetCentroid() const {
+ // When a sphere is divided into slices of constant thickness by a set of
+ // parallel planes, all slices have the same surface area. This implies
+ // that the z-component of the centroid is simply the midpoint of the
+ // z-interval spanned by the S2LatLngRect.
+ //
+ // Similarly, it is easy to see that the (x,y) of the centroid lies in the
+ // plane through the midpoint of the rectangle's longitude interval. We
+ // only need to determine the distance "d" of this point from the z-axis.
+ //
+ // Let's restrict our attention to a particular z-value. In this z-plane,
+ // the S2LatLngRect is a circular arc. The centroid of this arc lies on a
+ // radial line through the midpoint of the arc, and at a distance from the
+ // z-axis of
+ //
+ // r * (sin(alpha) / alpha)
+ //
+ // where r = sqrt(1-z^2) is the radius of the arc, and "alpha" is half of
+ // the arc length (i.e., the arc covers longitudes [-alpha, alpha]).
+ //
+ // To find the centroid distance from the z-axis for the entire rectangle,
+ // we just need to integrate over the z-interval. This gives
+ //
+ // d = Integrate[sqrt(1-z^2)*sin(alpha)/alpha, z1..z2] / (z2 - z1)
+ //
+ // where [z1, z2] is the range of z-values covered by the rectangle. This
+ // simplifies to
+ //
+ // d = sin(alpha)/(2*alpha*(z2-z1))*(z2*r2 - z1*r1 + theta2 - theta1)
+ //
+ // where [theta1, theta2] is the latitude interval, z1=sin(theta1),
+ // z2=sin(theta2), r1=cos(theta1), and r2=cos(theta2).
+ //
+ // Finally, we want to return not the centroid itself, but the centroid
+ // scaled by the area of the rectangle. The area of the rectangle is
+ //
+ // A = 2 * alpha * (z2 - z1)
+ //
+ // which fortunately appears in the denominator of "d".
+
+ if (is_empty()) return S2Point();
+ double z1 = sin(lat_lo()), z2 = sin(lat_hi());
+ double r1 = cos(lat_lo()), r2 = cos(lat_hi());
+ double alpha = 0.5 * lng_.GetLength();
+ double r = sin(alpha) * (r2 * z2 - r1 * z1 + lat_.GetLength());
+ double lng = lng_.GetCenter();
+ double z = alpha * (z2 + z1) * (z2 - z1); // scaled by the area
+ return S2Point(r * cos(lng), r * sin(lng), z);
+}
+
+bool S2LatLngRect::Contains(const S2LatLng& ll) const {
+ S2_DLOG_IF(ERROR, !ll.is_valid())
+ << "Invalid S2LatLng in S2LatLngRect::Contains: " << ll;
+
+ return (lat_.Contains(ll.lat().radians()) &&
+ lng_.Contains(ll.lng().radians()));
+}
+
+bool S2LatLngRect::InteriorContains(const S2Point& p) const {
+ return InteriorContains(S2LatLng(p));
+}
+
+bool S2LatLngRect::InteriorContains(const S2LatLng& ll) const {
+ S2_DLOG_IF(ERROR, !ll.is_valid())
+ << "Invalid S2LatLng in S2LatLngRect::InteriorContains: " << ll;
+
+ return (lat_.InteriorContains(ll.lat().radians()) &&
+ lng_.InteriorContains(ll.lng().radians()));
+}
+
+bool S2LatLngRect::Contains(const S2LatLngRect& other) const {
+ return lat_.Contains(other.lat_) && lng_.Contains(other.lng_);
+}
+
+bool S2LatLngRect::InteriorContains(const S2LatLngRect& other) const {
+ return (lat_.InteriorContains(other.lat_) &&
+ lng_.InteriorContains(other.lng_));
+}
+
+bool S2LatLngRect::Intersects(const S2LatLngRect& other) const {
+ return lat_.Intersects(other.lat_) && lng_.Intersects(other.lng_);
+}
+
+bool S2LatLngRect::InteriorIntersects(const S2LatLngRect& other) const {
+ return (lat_.InteriorIntersects(other.lat_) &&
+ lng_.InteriorIntersects(other.lng_));
+}
+
+bool S2LatLngRect::BoundaryIntersects(const S2Point& v0,
+ const S2Point& v1) const {
+ if (is_empty()) return false;
+ if (!lng_.is_full()) {
+ if (IntersectsLngEdge(v0, v1, lat_, lng_.lo())) return true;
+ if (IntersectsLngEdge(v0, v1, lat_, lng_.hi())) return true;
+ }
+ if (lat_.lo() != -M_PI_2 && IntersectsLatEdge(v0, v1, lat_.lo(), lng_)) {
+ return true;
+ }
+ if (lat_.hi() != M_PI_2 && IntersectsLatEdge(v0, v1, lat_.hi(), lng_)) {
+ return true;
+ }
+ return false;
+}
+
+void S2LatLngRect::AddPoint(const S2Point& p) {
+ AddPoint(S2LatLng(p));
+}
+
+void S2LatLngRect::AddPoint(const S2LatLng& ll) {
+ S2_DLOG_IF(ERROR, !ll.is_valid())
+ << "Invalid S2LatLng in S2LatLngRect::AddPoint: " << ll;
+
+ lat_.AddPoint(ll.lat().radians());
+ lng_.AddPoint(ll.lng().radians());
+}
+
+S2LatLngRect S2LatLngRect::Expanded(const S2LatLng& margin) const {
+ R1Interval lat = lat_.Expanded(margin.lat().radians());
+ S1Interval lng = lng_.Expanded(margin.lng().radians());
+ if (lat.is_empty() || lng.is_empty()) return Empty();
+ return S2LatLngRect(lat.Intersection(FullLat()), lng);
+}
+
+S2LatLngRect S2LatLngRect::PolarClosure() const {
+ if (lat_.lo() == -M_PI_2 || lat_.hi() == M_PI_2) {
+ return S2LatLngRect(lat_, S1Interval::Full());
+ }
+ return *this;
+}
+
+S2LatLngRect S2LatLngRect::Union(const S2LatLngRect& other) const {
+ return S2LatLngRect(lat_.Union(other.lat_),
+ lng_.Union(other.lng_));
+}
+
+S2LatLngRect S2LatLngRect::Intersection(const S2LatLngRect& other) const {
+ R1Interval lat = lat_.Intersection(other.lat_);
+ S1Interval lng = lng_.Intersection(other.lng_);
+ if (lat.is_empty() || lng.is_empty()) {
+ // The lat/lng ranges must either be both empty or both non-empty.
+ return Empty();
+ }
+ return S2LatLngRect(lat, lng);
+}
+
+S2LatLngRect S2LatLngRect::ExpandedByDistance(S1Angle distance) const {
+ if (distance >= S1Angle::Zero()) {
+ // The most straightforward approach is to build a cap centered on each
+ // vertex and take the union of all the bounding rectangles (including the
+ // original rectangle; this is necessary for very large rectangles).
+
+ // TODO(ericv): Update this code to use an algorithm like the one below.
+ S1ChordAngle radius(distance);
+ S2LatLngRect r = *this;
+ for (int k = 0; k < 4; ++k) {
+ r = r.Union(S2Cap(GetVertex(k).ToPoint(), radius).GetRectBound());
+ }
+ return r;
+ } else {
+ // Shrink the latitude interval unless the latitude interval contains a pole
+ // and the longitude interval is full, in which case the rectangle has no
+ // boundary at that pole.
+ R1Interval lat_result(
+ lat().lo() <= FullLat().lo() && lng().is_full() ?
+ FullLat().lo() : lat().lo() - distance.radians(),
+ lat().hi() >= FullLat().hi() && lng().is_full() ?
+ FullLat().hi() : lat().hi() + distance.radians());
+ if (lat_result.is_empty()) {
+ return S2LatLngRect::Empty();
+ }
+
+ // Maximum absolute value of a latitude in lat_result. At this latitude,
+ // the cap occupies the largest longitude interval.
+ double max_abs_lat = max(-lat_result.lo(), lat_result.hi());
+
+ // Compute the largest longitude interval that the cap occupies. We use the
+ // law of sines for spherical triangles. For the details, see the comment in
+ // S2Cap::GetRectBound().
+ //
+ // When sin_a >= sin_c, the cap covers all the latitude.
+ double sin_a = sin(-distance.radians());
+ double sin_c = cos(max_abs_lat);
+ double max_lng_margin = sin_a < sin_c ? asin(sin_a / sin_c) : M_PI_2;
+
+ S1Interval lng_result = lng().Expanded(-max_lng_margin);
+ if (lng_result.is_empty()) {
+ return S2LatLngRect::Empty();
+ }
+ return S2LatLngRect(lat_result, lng_result);
+ }
+}
+
+S2Cap S2LatLngRect::GetCapBound() const {
+ // We consider two possible bounding caps, one whose axis passes
+ // through the center of the lat-long rectangle and one whose axis
+ // is the north or south pole. We return the smaller of the two caps.
+
+ if (is_empty()) return S2Cap::Empty();
+
+ double pole_z, pole_angle;
+ if (lat_.lo() + lat_.hi() < 0) {
+ // South pole axis yields smaller cap.
+ pole_z = -1;
+ pole_angle = M_PI_2 + lat_.hi();
+ } else {
+ pole_z = 1;
+ pole_angle = M_PI_2 - lat_.lo();
+ }
+ S2Cap pole_cap(S2Point(0, 0, pole_z), S1Angle::Radians(pole_angle));
+
+ // For bounding rectangles that span 180 degrees or less in longitude, the
+ // maximum cap size is achieved at one of the rectangle vertices. For
+ // rectangles that are larger than 180 degrees, we punt and always return a
+ // bounding cap centered at one of the two poles.
+ double lng_span = lng_.hi() - lng_.lo();
+ if (remainder(lng_span, 2 * M_PI) >= 0 && lng_span < 2 * M_PI) {
+ S2Cap mid_cap(GetCenter().ToPoint(), S1Angle::Radians(0));
+ for (int k = 0; k < 4; ++k) {
+ mid_cap.AddPoint(GetVertex(k).ToPoint());
+ }
+ if (mid_cap.height() < pole_cap.height())
+ return mid_cap;
+ }
+ return pole_cap;
+}
+
+S2LatLngRect S2LatLngRect::GetRectBound() const {
+ return *this;
+}
+
+bool S2LatLngRect::Contains(const S2Cell& cell) const {
+ // A latitude-longitude rectangle contains a cell if and only if it contains
+ // the cell's bounding rectangle. This test is exact from a mathematical
+ // point of view, assuming that the bounds returned by S2Cell::GetRectBound()
+ // are tight. However, note that there can be a loss of precision when
+ // converting between representations -- for example, if an S2Cell is
+ // converted to a polygon, the polygon's bounding rectangle may not contain
+ // the cell's bounding rectangle. This has some slightly unexpected side
+ // effects; for instance, if one creates an S2Polygon from an S2Cell, the
+ // polygon will contain the cell, but the polygon's bounding box will not.
+ return Contains(cell.GetRectBound());
+}
+
+bool S2LatLngRect::MayIntersect(const S2Cell& cell) const {
+ // This test is cheap but is NOT exact (see s2latlng_rect.h).
+ return Intersects(cell.GetRectBound());
+}
+
+void S2LatLngRect::Encode(Encoder* encoder) const {
+ encoder->Ensure(40); // sufficient
+
+ encoder->put8(kCurrentLosslessEncodingVersionNumber);
+ encoder->putdouble(lat_.lo());
+ encoder->putdouble(lat_.hi());
+ encoder->putdouble(lng_.lo());
+ encoder->putdouble(lng_.hi());
+
+ S2_DCHECK_GE(encoder->avail(), 0);
+}
+
+bool S2LatLngRect::Decode(Decoder* decoder) {
+ if (decoder->avail() < sizeof(unsigned char) + 4 * sizeof(double))
+ return false;
+ unsigned char version = decoder->get8();
+ if (version > kCurrentLosslessEncodingVersionNumber) return false;
+
+ double lat_lo = decoder->getdouble();
+ double lat_hi = decoder->getdouble();
+ lat_ = R1Interval(lat_lo, lat_hi);
+ double lng_lo = decoder->getdouble();
+ double lng_hi = decoder->getdouble();
+ lng_ = S1Interval(lng_lo, lng_hi);
+
+ if (!is_valid()) {
+ S2_DLOG_IF(ERROR, FLAGS_s2debug)
+ << "Invalid result in S2LatLngRect::Decode: " << *this;
+ return false;
+ }
+
+ return true;
+}
+
+bool S2LatLngRect::IntersectsLngEdge(const S2Point& a, const S2Point& b,
+ const R1Interval& lat, double lng) {
+ // Return true if the segment AB intersects the given edge of constant
+ // longitude. The nice thing about edges of constant longitude is that
+ // they are straight lines on the sphere (geodesics).
+
+ return S2::CrossingSign(
+ a, b, S2LatLng::FromRadians(lat.lo(), lng).ToPoint(),
+ S2LatLng::FromRadians(lat.hi(), lng).ToPoint()) > 0;
+}
+
+bool S2LatLngRect::IntersectsLatEdge(const S2Point& a, const S2Point& b,
+ double lat, const S1Interval& lng) {
+ // Return true if the segment AB intersects the given edge of constant
+ // latitude. Unfortunately, lines of constant latitude are curves on
+ // the sphere. They can intersect a straight edge in 0, 1, or 2 points.
+ S2_DCHECK(S2::IsUnitLength(a));
+ S2_DCHECK(S2::IsUnitLength(b));
+
+ // First, compute the normal to the plane AB that points vaguely north.
+ Vector3_d z = S2::RobustCrossProd(a, b).Normalize();
+ if (z[2] < 0) z = -z;
+
+ // Extend this to an orthonormal frame (x,y,z) where x is the direction
+ // where the great circle through AB achieves its maximium latitude.
+ Vector3_d y = S2::RobustCrossProd(z, S2Point(0, 0, 1)).Normalize();
+ Vector3_d x = y.CrossProd(z);
+ S2_DCHECK(S2::IsUnitLength(x));
+ S2_DCHECK_GE(x[2], 0);
+
+ // Compute the angle "theta" from the x-axis (in the x-y plane defined
+ // above) where the great circle intersects the given line of latitude.
+ double sin_lat = sin(lat);
+ if (fabs(sin_lat) >= x[2]) {
+ return false; // The great circle does not reach the given latitude.
+ }
+ S2_DCHECK_GT(x[2], 0);
+ double cos_theta = sin_lat / x[2];
+ double sin_theta = sqrt(1 - cos_theta * cos_theta);
+ double theta = atan2(sin_theta, cos_theta);
+
+ // The candidate intersection points are located +/- theta in the x-y
+ // plane. For an intersection to be valid, we need to check that the
+ // intersection point is contained in the interior of the edge AB and
+ // also that it is contained within the given longitude interval "lng".
+
+ // Compute the range of theta values spanned by the edge AB.
+ S1Interval ab_theta = S1Interval::FromPointPair(
+ atan2(a.DotProd(y), a.DotProd(x)),
+ atan2(b.DotProd(y), b.DotProd(x)));
+
+ if (ab_theta.Contains(theta)) {
+ // Check if the intersection point is also in the given "lng" interval.
+ S2Point isect = x * cos_theta + y * sin_theta;
+ if (lng.Contains(atan2(isect[1], isect[0]))) return true;
+ }
+ if (ab_theta.Contains(-theta)) {
+ // Check if the intersection point is also in the given "lng" interval.
+ S2Point isect = x * cos_theta - y * sin_theta;
+ if (lng.Contains(atan2(isect[1], isect[0]))) return true;
+ }
+ return false;
+}
+
+bool S2LatLngRect::Intersects(const S2Cell& cell) const {
+ // First we eliminate the cases where one region completely contains the
+ // other. Once these are disposed of, then the regions will intersect
+ // if and only if their boundaries intersect.
+
+ if (is_empty()) return false;
+ if (Contains(cell.GetCenterRaw())) return true;
+ if (cell.Contains(GetCenter().ToPoint())) return true;
+
+ // Quick rejection test (not required for correctness).
+ if (!Intersects(cell.GetRectBound())) return false;
+
+ // Precompute the cell vertices as points and latitude-longitudes. We also
+ // check whether the S2Cell contains any corner of the rectangle, or
+ // vice-versa, since the edge-crossing tests only check the edge interiors.
+
+ S2Point cell_v[4];
+ S2LatLng cell_ll[4];
+ for (int i = 0; i < 4; ++i) {
+ cell_v[i] = cell.GetVertex(i); // Must be normalized.
+ cell_ll[i] = S2LatLng(cell_v[i]);
+ if (Contains(cell_ll[i])) return true;
+ if (cell.Contains(GetVertex(i).ToPoint())) return true;
+ }
+
+ // Now check whether the boundaries intersect. Unfortunately, a
+ // latitude-longitude rectangle does not have straight edges -- two edges
+ // are curved, and at least one of them is concave.
+
+ for (int i = 0; i < 4; ++i) {
+ S1Interval edge_lng = S1Interval::FromPointPair(
+ cell_ll[i].lng().radians(), cell_ll[(i+1)&3].lng().radians());
+ if (!lng_.Intersects(edge_lng)) continue;
+
+ const S2Point& a = cell_v[i];
+ const S2Point& b = cell_v[(i+1)&3];
+ if (edge_lng.Contains(lng_.lo())) {
+ if (IntersectsLngEdge(a, b, lat_, lng_.lo())) return true;
+ }
+ if (edge_lng.Contains(lng_.hi())) {
+ if (IntersectsLngEdge(a, b, lat_, lng_.hi())) return true;
+ }
+ if (IntersectsLatEdge(a, b, lat_.lo(), lng_)) return true;
+ if (IntersectsLatEdge(a, b, lat_.hi(), lng_)) return true;
+ }
+ return false;
+}
+
+S1Angle S2LatLngRect::GetDistance(const S2LatLngRect& other) const {
+ const S2LatLngRect& a = *this;
+ const S2LatLngRect& b = other;
+ S2_DCHECK(!a.is_empty());
+ S2_DCHECK(!b.is_empty());
+
+ // First, handle the trivial cases where the longitude intervals overlap.
+ if (a.lng().Intersects(b.lng())) {
+ if (a.lat().Intersects(b.lat()))
+ return S1Angle::Radians(0); // Intersection between a and b.
+
+ // We found an overlap in the longitude interval, but not in the latitude
+ // interval. This means the shortest path travels along some line of
+ // longitude connecting the high-latitude of the lower rect with the
+ // low-latitude of the higher rect.
+ S1Angle lo, hi;
+ if (a.lat().lo() > b.lat().hi()) {
+ lo = b.lat_hi();
+ hi = a.lat_lo();
+ } else {
+ lo = a.lat_hi();
+ hi = b.lat_lo();
+ }
+ return hi - lo;
+ }
+
+ // The longitude intervals don't overlap. In this case, the closest points
+ // occur somewhere on the pair of longitudinal edges which are nearest in
+ // longitude-space.
+ S1Angle a_lng, b_lng;
+ S1Interval lo_hi = S1Interval::FromPointPair(a.lng().lo(), b.lng().hi());
+ S1Interval hi_lo = S1Interval::FromPointPair(a.lng().hi(), b.lng().lo());
+ if (lo_hi.GetLength() < hi_lo.GetLength()) {
+ a_lng = a.lng_lo();
+ b_lng = b.lng_hi();
+ } else {
+ a_lng = a.lng_hi();
+ b_lng = b.lng_lo();
+ }
+
+ // The shortest distance between the two longitudinal segments will include at
+ // least one segment endpoint. We could probably narrow this down further to a
+ // single point-edge distance by comparing the relative latitudes of the
+ // endpoints, but for the sake of clarity, we'll do all four point-edge
+ // distance tests.
+ S2Point a_lo = S2LatLng(a.lat_lo(), a_lng).ToPoint();
+ S2Point a_hi = S2LatLng(a.lat_hi(), a_lng).ToPoint();
+ S2Point b_lo = S2LatLng(b.lat_lo(), b_lng).ToPoint();
+ S2Point b_hi = S2LatLng(b.lat_hi(), b_lng).ToPoint();
+ return min(S2::GetDistance(a_lo, b_lo, b_hi),
+ min(S2::GetDistance(a_hi, b_lo, b_hi),
+ min(S2::GetDistance(b_lo, a_lo, a_hi),
+ S2::GetDistance(b_hi, a_lo, a_hi))));
+}
+
+S1Angle S2LatLngRect::GetDistance(const S2LatLng& p) const {
+ // The algorithm here is the same as in GetDistance(S2LatLngRect), only
+ // with simplified calculations.
+ const S2LatLngRect& a = *this;
+ S2_DLOG_IF(ERROR, a.is_empty())
+ << "Empty S2LatLngRect in S2LatLngRect::GetDistance: " << a;
+
+ S2_DLOG_IF(ERROR, !p.is_valid())
+ << "Invalid S2LatLng in S2LatLngRect::GetDistance: " << p;
+
+ if (a.lng().Contains(p.lng().radians())) {
+ return S1Angle::Radians(max(0.0, max(p.lat().radians() - a.lat().hi(),
+ a.lat().lo() - p.lat().radians())));
+ }
+
+ S1Interval interval(a.lng().hi(), a.lng().GetComplementCenter());
+ double a_lng;
+ if (interval.Contains(p.lng().radians())) {
+ a_lng = a.lng().hi();
+ } else {
+ a_lng = a.lng().lo();
+ }
+ S2Point lo = S2LatLng::FromRadians(a.lat().lo(), a_lng).ToPoint();
+ S2Point hi = S2LatLng::FromRadians(a.lat().hi(), a_lng).ToPoint();
+ return S2::GetDistance(p.ToPoint(), lo, hi);
+}
+
+S1Angle S2LatLngRect::GetHausdorffDistance(const S2LatLngRect& other) const {
+ return max(GetDirectedHausdorffDistance(other),
+ other.GetDirectedHausdorffDistance(*this));
+}
+
+S1Angle S2LatLngRect::GetDirectedHausdorffDistance(
+ const S2LatLngRect& other) const {
+ if (is_empty()) {
+ return S1Angle::Radians(0);
+ }
+ if (other.is_empty()) {
+ return S1Angle::Radians(M_PI); // maximum possible distance on S2
+ }
+
+ double lng_distance = lng().GetDirectedHausdorffDistance(other.lng());
+ S2_DCHECK_GE(lng_distance, 0);
+ return GetDirectedHausdorffDistance(lng_distance, lat(), other.lat());
+}
+
+// Return the directed Hausdorff distance from one longitudinal edge spanning
+// latitude range 'a_lat' to the other longitudinal edge spanning latitude
+// range 'b_lat', with their longitudinal difference given by 'lng_diff'.
+S1Angle S2LatLngRect::GetDirectedHausdorffDistance(
+ double lng_diff, const R1Interval& a, const R1Interval& b) {
+ // By symmetry, we can assume a's longtitude is 0 and b's longtitude is
+ // lng_diff. Call b's two endpoints b_lo and b_hi. Let H be the hemisphere
+ // containing a and delimited by the longitude line of b. The Voronoi diagram
+ // of b on H has three edges (portions of great circles) all orthogonal to b
+ // and meeting at b_lo cross b_hi.
+ // E1: (b_lo, b_lo cross b_hi)
+ // E2: (b_hi, b_lo cross b_hi)
+ // E3: (-b_mid, b_lo cross b_hi), where b_mid is the midpoint of b
+ //
+ // They subdivide H into three Voronoi regions. Depending on how longitude 0
+ // (which contains edge a) intersects these regions, we distinguish two cases:
+ // Case 1: it intersects three regions. This occurs when lng_diff <= M_PI_2.
+ // Case 2: it intersects only two regions. This occurs when lng_diff > M_PI_2.
+ //
+ // In the first case, the directed Hausdorff distance to edge b can only be
+ // realized by the following points on a:
+ // A1: two endpoints of a.
+ // A2: intersection of a with the equator, if b also intersects the equator.
+ //
+ // In the second case, the directed Hausdorff distance to edge b can only be
+ // realized by the following points on a:
+ // B1: two endpoints of a.
+ // B2: intersection of a with E3
+ // B3: farthest point from b_lo to the interior of D, and farthest point from
+ // b_hi to the interior of U, if any, where D (resp. U) is the portion
+ // of edge a below (resp. above) the intersection point from B2.
+
+ S2_DCHECK_GE(lng_diff, 0);
+ S2_DCHECK_LE(lng_diff, M_PI);
+
+ if (lng_diff == 0) {
+ return S1Angle::Radians(a.GetDirectedHausdorffDistance(b));
+ }
+
+ // Assumed longtitude of b.
+ double b_lng = lng_diff;
+ // Two endpoints of b.
+ S2Point b_lo = S2LatLng::FromRadians(b.lo(), b_lng).ToPoint();
+ S2Point b_hi = S2LatLng::FromRadians(b.hi(), b_lng).ToPoint();
+
+ // Handling of each case outlined at the top of the function starts here.
+ // This is initialized a few lines below.
+ S1Angle max_distance;
+
+ // Cases A1 and B1.
+ S2Point a_lo = S2LatLng::FromRadians(a.lo(), 0).ToPoint();
+ S2Point a_hi = S2LatLng::FromRadians(a.hi(), 0).ToPoint();
+ max_distance = S2::GetDistance(a_lo, b_lo, b_hi);
+ max_distance = max(
+ max_distance, S2::GetDistance(a_hi, b_lo, b_hi));
+
+ if (lng_diff <= M_PI_2) {
+ // Case A2.
+ if (a.Contains(0) && b.Contains(0)) {
+ max_distance = max(max_distance, S1Angle::Radians(lng_diff));
+ }
+ } else {
+ // Case B2.
+ const S2Point& p = GetBisectorIntersection(b, b_lng);
+ double p_lat = S2LatLng::Latitude(p).radians();
+ if (a.Contains(p_lat)) {
+ max_distance = max(max_distance, S1Angle(p, b_lo));
+ }
+
+ // Case B3.
+ if (p_lat > a.lo()) {
+ max_distance = max(max_distance, GetInteriorMaxDistance(
+ R1Interval(a.lo(), min(p_lat, a.hi())), b_lo));
+ }
+ if (p_lat < a.hi()) {
+ max_distance = max(max_distance, GetInteriorMaxDistance(
+ R1Interval(max(p_lat, a.lo()), a.hi()), b_hi));
+ }
+ }
+
+ return max_distance;
+}
+
+// Return the intersection of longitude 0 with the bisector of an edge
+// on longitude 'lng' and spanning latitude range 'lat'.
+S2Point S2LatLngRect::GetBisectorIntersection(const R1Interval& lat,
+ double lng) {
+ lng = fabs(lng);
+ double lat_center = lat.GetCenter();
+ // A vector orthogonal to the bisector of the given longitudinal edge.
+ S2LatLng ortho_bisector;
+ if (lat_center >= 0) {
+ ortho_bisector = S2LatLng::FromRadians(lat_center - M_PI_2, lng);
+ } else {
+ ortho_bisector = S2LatLng::FromRadians(-lat_center - M_PI_2, lng - M_PI);
+ }
+ // A vector orthogonal to longitude 0.
+ static const S2Point ortho_lng = S2Point(0, -1, 0);
+ return S2::RobustCrossProd(ortho_lng, ortho_bisector.ToPoint());
+}
+
+// Return max distance from a point b to the segment spanning latitude range
+// a_lat on longitude 0, if the max occurs in the interior of a_lat. Otherwise
+// return -1.
+S1Angle S2LatLngRect::GetInteriorMaxDistance(const R1Interval& a_lat,
+ const S2Point& b) {
+ // Longitude 0 is in the y=0 plane. b.x() >= 0 implies that the maximum
+ // does not occur in the interior of a_lat.
+ if (a_lat.is_empty() || b.x() >= 0) return S1Angle::Radians(-1);
+
+ // Project b to the y=0 plane. The antipodal of the normalized projection is
+ // the point at which the maxium distance from b occurs, if it is contained
+ // in a_lat.
+ S2Point intersection_point = S2Point(-b.x(), 0, -b.z()).Normalize();
+ if (a_lat.InteriorContains(
+ S2LatLng::Latitude(intersection_point).radians())) {
+ return S1Angle(b, intersection_point);
+ } else {
+ return S1Angle::Radians(-1);
+ }
+}
+
+bool S2LatLngRect::Contains(const S2Point& p) const {
+ return Contains(S2LatLng(p));
+}
+
+bool S2LatLngRect::ApproxEquals(const S2LatLngRect& other,
+ S1Angle max_error) const {
+ return (lat_.ApproxEquals(other.lat_, max_error.radians()) &&
+ lng_.ApproxEquals(other.lng_, max_error.radians()));
+}
+
+bool S2LatLngRect::ApproxEquals(const S2LatLngRect& other,
+ const S2LatLng& max_error) const {
+ return (lat_.ApproxEquals(other.lat_, max_error.lat().radians()) &&
+ lng_.ApproxEquals(other.lng_, max_error.lng().radians()));
+}
+
+std::ostream& operator<<(std::ostream& os, const S2LatLngRect& r) {
+ return os << "[Lo" << r.lo() << ", Hi" << r.hi() << "]";
+}
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2latlng_rect_bounder.h"
+
+#include <cfloat>
+#include <cmath>
+
+#include "s2/base/logging.h"
+#include "s2/r1interval.h"
+#include "s2/s1chord_angle.h"
+#include "s2/s1interval.h"
+#include "s2/s2pointutil.h"
+
+using std::fabs;
+using std::max;
+using std::min;
+
+void S2LatLngRectBounder::AddPoint(const S2Point& b) {
+ S2_DCHECK(S2::IsUnitLength(b));
+ AddInternal(b, S2LatLng(b));
+}
+
+void S2LatLngRectBounder::AddLatLng(const S2LatLng& b_latlng) {
+ AddInternal(b_latlng.ToPoint(), b_latlng);
+}
+
+void S2LatLngRectBounder::AddInternal(const S2Point& b,
+ const S2LatLng& b_latlng) {
+ // Simple consistency check to verify that b and b_latlng are alternate
+ // representations of the same vertex.
+ S2_DCHECK(S2::ApproxEquals(b, b_latlng.ToPoint()));
+
+ if (bound_.is_empty()) {
+ bound_.AddPoint(b_latlng);
+ } else {
+ // First compute the cross product N = A x B robustly. This is the normal
+ // to the great circle through A and B. We don't use S2::RobustCrossProd()
+ // since that method returns an arbitrary vector orthogonal to A if the two
+ // vectors are proportional, and we want the zero vector in that case.
+ Vector3_d n = (a_ - b).CrossProd(a_ + b); // N = 2 * (A x B)
+
+ // The relative error in N gets large as its norm gets very small (i.e.,
+ // when the two points are nearly identical or antipodal). We handle this
+ // by choosing a maximum allowable error, and if the error is greater than
+ // this we fall back to a different technique. Since it turns out that
+ // the other sources of error in converting the normal to a maximum
+ // latitude add up to at most 1.16 * DBL_EPSILON (see below), and it is
+ // desirable to have the total error be a multiple of DBL_EPSILON, we have
+ // chosen to limit the maximum error in the normal to 3.84 * DBL_EPSILON.
+ // It is possible to show that the error is less than this when
+ //
+ // n.Norm() >= 8 * sqrt(3) / (3.84 - 0.5 - sqrt(3)) * DBL_EPSILON
+ // = 1.91346e-15 (about 8.618 * DBL_EPSILON)
+ double n_norm = n.Norm();
+ if (n_norm < 1.91346e-15) {
+ // A and B are either nearly identical or nearly antipodal (to within
+ // 4.309 * DBL_EPSILON, or about 6 nanometers on the earth's surface).
+ if (a_.DotProd(b) < 0) {
+ // The two points are nearly antipodal. The easiest solution is to
+ // assume that the edge between A and B could go in any direction
+ // around the sphere.
+ bound_ = S2LatLngRect::Full();
+ } else {
+ // The two points are nearly identical (to within 4.309 * DBL_EPSILON).
+ // In this case we can just use the bounding rectangle of the points,
+ // since after the expansion done by GetBound() this rectangle is
+ // guaranteed to include the (lat,lng) values of all points along AB.
+ bound_ = bound_.Union(S2LatLngRect::FromPointPair(a_latlng_, b_latlng));
+ }
+ } else {
+ // Compute the longitude range spanned by AB.
+ S1Interval lng_ab = S1Interval::FromPointPair(a_latlng_.lng().radians(),
+ b_latlng.lng().radians());
+ if (lng_ab.GetLength() >= M_PI - 2 * DBL_EPSILON) {
+ // The points lie on nearly opposite lines of longitude to within the
+ // maximum error of the calculation. (Note that this test relies on
+ // the fact that M_PI is slightly less than the true value of Pi, and
+ // that representable values near M_PI are 2 * DBL_EPSILON apart.)
+ // The easiest solution is to assume that AB could go on either side
+ // of the pole.
+ lng_ab = S1Interval::Full();
+ }
+
+ // Next we compute the latitude range spanned by the edge AB. We start
+ // with the range spanning the two endpoints of the edge:
+ R1Interval lat_ab = R1Interval::FromPointPair(a_latlng_.lat().radians(),
+ b_latlng.lat().radians());
+
+ // This is the desired range unless the edge AB crosses the plane
+ // through N and the Z-axis (which is where the great circle through A
+ // and B attains its minimum and maximum latitudes). To test whether AB
+ // crosses this plane, we compute a vector M perpendicular to this
+ // plane and then project A and B onto it.
+ Vector3_d m = n.CrossProd(S2Point(0, 0, 1));
+ double m_a = m.DotProd(a_);
+ double m_b = m.DotProd(b);
+
+ // We want to test the signs of "m_a" and "m_b", so we need to bound
+ // the error in these calculations. It is possible to show that the
+ // total error is bounded by
+ //
+ // (1 + sqrt(3)) * DBL_EPSILON * n_norm + 8 * sqrt(3) * (DBL_EPSILON**2)
+ // = 6.06638e-16 * n_norm + 6.83174e-31
+
+ double m_error = 6.06638e-16 * n_norm + 6.83174e-31;
+ if (m_a * m_b < 0 || fabs(m_a) <= m_error || fabs(m_b) <= m_error) {
+ // Minimum/maximum latitude *may* occur in the edge interior.
+ //
+ // The maximum latitude is 90 degrees minus the latitude of N. We
+ // compute this directly using atan2 in order to get maximum accuracy
+ // near the poles.
+ //
+ // Our goal is compute a bound that contains the computed latitudes of
+ // all S2Points P that pass the point-in-polygon containment test.
+ // There are three sources of error we need to consider:
+ // - the directional error in N (at most 3.84 * DBL_EPSILON)
+ // - converting N to a maximum latitude
+ // - computing the latitude of the test point P
+ // The latter two sources of error are at most 0.955 * DBL_EPSILON
+ // individually, but it is possible to show by a more complex analysis
+ // that together they can add up to at most 1.16 * DBL_EPSILON, for a
+ // total error of 5 * DBL_EPSILON.
+ //
+ // We add 3 * DBL_EPSILON to the bound here, and GetBound() will pad
+ // the bound by another 2 * DBL_EPSILON.
+ double max_lat = min(
+ atan2(sqrt(n[0]*n[0] + n[1]*n[1]), fabs(n[2])) + 3 * DBL_EPSILON,
+ M_PI_2);
+
+ // In order to get tight bounds when the two points are close together,
+ // we also bound the min/max latitude relative to the latitudes of the
+ // endpoints A and B. First we compute the distance between A and B,
+ // and then we compute the maximum change in latitude between any two
+ // points along the great circle that are separated by this distance.
+ // This gives us a latitude change "budget". Some of this budget must
+ // be spent getting from A to B; the remainder bounds the round-trip
+ // distance (in latitude) from A or B to the min or max latitude
+ // attained along the edge AB.
+ double lat_budget = 2 * asin(0.5 * (a_ - b).Norm() * sin(max_lat));
+ double max_delta = 0.5*(lat_budget - lat_ab.GetLength()) + DBL_EPSILON;
+
+ // Test whether AB passes through the point of maximum latitude or
+ // minimum latitude. If the dot product(s) are small enough then the
+ // result may be ambiguous.
+ if (m_a <= m_error && m_b >= -m_error) {
+ lat_ab.set_hi(min(max_lat, lat_ab.hi() + max_delta));
+ }
+ if (m_b <= m_error && m_a >= -m_error) {
+ lat_ab.set_lo(max(-max_lat, lat_ab.lo() - max_delta));
+ }
+ }
+ bound_ = bound_.Union(S2LatLngRect(lat_ab, lng_ab));
+ }
+ }
+ a_ = b;
+ a_latlng_ = b_latlng;
+}
+
+S2LatLngRect S2LatLngRectBounder::GetBound() const {
+ // To save time, we ignore numerical errors in the computed S2LatLngs while
+ // accumulating the bounds and then account for them here.
+ //
+ // S2LatLng(S2Point) has a maximum error of 0.955 * DBL_EPSILON in latitude.
+ // In the worst case, we might have rounded "inwards" when computing the
+ // bound and "outwards" when computing the latitude of a contained point P,
+ // therefore we expand the latitude bounds by 2 * DBL_EPSILON in each
+ // direction. (A more complex analysis shows that 1.5 * DBL_EPSILON is
+ // enough, but the expansion amount should be a multiple of DBL_EPSILON in
+ // order to avoid rounding errors during the expansion itself.)
+ //
+ // S2LatLng(S2Point) has a maximum error of DBL_EPSILON in longitude, which
+ // is simply the maximum rounding error for results in the range [-Pi, Pi].
+ // This is true because the Gnu implementation of atan2() comes from the IBM
+ // Accurate Mathematical Library, which implements correct rounding for this
+ // instrinsic (i.e., it returns the infinite precision result rounded to the
+ // nearest representable value, with ties rounded to even values). This
+ // implies that we don't need to expand the longitude bounds at all, since
+ // we only guarantee that the bound contains the *rounded* latitudes of
+ // contained points. The *true* latitudes of contained points may lie up to
+ // DBL_EPSILON outside of the returned bound.
+
+ const S2LatLng kExpansion = S2LatLng::FromRadians(2 * DBL_EPSILON, 0);
+ return bound_.Expanded(kExpansion).PolarClosure();
+}
+
+S2LatLngRect S2LatLngRectBounder::ExpandForSubregions(
+ const S2LatLngRect& bound) {
+ // Empty bounds don't need expansion.
+ if (bound.is_empty()) return bound;
+
+ // First we need to check whether the bound B contains any nearly-antipodal
+ // points (to within 4.309 * DBL_EPSILON). If so then we need to return
+ // S2LatLngRect::Full(), since the subregion might have an edge between two
+ // such points, and AddPoint() returns Full() for such edges. Note that
+ // this can happen even if B is not Full(); for example, consider a loop
+ // that defines a 10km strip straddling the equator extending from
+ // longitudes -100 to +100 degrees.
+ //
+ // It is easy to check whether B contains any antipodal points, but checking
+ // for nearly-antipodal points is trickier. Essentially we consider the
+ // original bound B and its reflection through the origin B', and then test
+ // whether the minimum distance between B and B' is less than 4.309 *
+ // DBL_EPSILON.
+
+ // "lng_gap" is a lower bound on the longitudinal distance between B and its
+ // reflection B'. (2.5 * DBL_EPSILON is the maximum combined error of the
+ // endpoint longitude calculations and the GetLength() call.)
+ double lng_gap = max(0.0, M_PI - bound.lng().GetLength() - 2.5 * DBL_EPSILON);
+
+ // "min_abs_lat" is the minimum distance from B to the equator (if zero or
+ // negative, then B straddles the equator).
+ double min_abs_lat = max(bound.lat().lo(), -bound.lat().hi());
+
+ // "lat_gap1" and "lat_gap2" measure the minimum distance from B to the
+ // south and north poles respectively.
+ double lat_gap1 = M_PI_2 + bound.lat().lo();
+ double lat_gap2 = M_PI_2 - bound.lat().hi();
+
+ if (min_abs_lat >= 0) {
+ // The bound B does not straddle the equator. In this case the minimum
+ // distance is between one endpoint of the latitude edge in B closest to
+ // the equator and the other endpoint of that edge in B'. The latitude
+ // distance between these two points is 2*min_abs_lat, and the longitude
+ // distance is lng_gap. We could compute the distance exactly using the
+ // Haversine formula, but then we would need to bound the errors in that
+ // calculation. Since we only need accuracy when the distance is very
+ // small (close to 4.309 * DBL_EPSILON), we substitute the Euclidean
+ // distance instead. This gives us a right triangle XYZ with two edges of
+ // length x = 2*min_abs_lat and y ~= lng_gap. The desired distance is the
+ // length of the third edge "z", and we have
+ //
+ // z ~= sqrt(x^2 + y^2) >= (x + y) / sqrt(2)
+ //
+ // Therefore the region may contain nearly antipodal points only if
+ //
+ // 2*min_abs_lat + lng_gap < sqrt(2) * 4.309 * DBL_EPSILON
+ // ~= 1.354e-15
+ //
+ // Note that because the given bound B is conservative, "min_abs_lat" and
+ // "lng_gap" are both lower bounds on their true values so we do not need
+ // to make any adjustments for their errors.
+ if (2 * min_abs_lat + lng_gap < 1.354e-15) {
+ return S2LatLngRect::Full();
+ }
+ } else if (lng_gap >= M_PI_2) {
+ // B spans at most Pi/2 in longitude. The minimum distance is always
+ // between one corner of B and the diagonally opposite corner of B'. We
+ // use the same distance approximation that we used above; in this case
+ // we have an obtuse triangle XYZ with two edges of length x = lat_gap1
+ // and y = lat_gap2, and angle Z >= Pi/2 between them. We then have
+ //
+ // z >= sqrt(x^2 + y^2) >= (x + y) / sqrt(2)
+ //
+ // Unlike the case above, "lat_gap1" and "lat_gap2" are not lower bounds
+ // (because of the extra addition operation, and because M_PI_2 is not
+ // exactly equal to Pi/2); they can exceed their true values by up to
+ // 0.75 * DBL_EPSILON. Putting this all together, the region may
+ // contain nearly antipodal points only if
+ //
+ // lat_gap1 + lat_gap2 < (sqrt(2) * 4.309 + 1.5) * DBL_EPSILON
+ // ~= 1.687e-15
+ if (lat_gap1 + lat_gap2 < 1.687e-15) {
+ return S2LatLngRect::Full();
+ }
+ } else {
+ // Otherwise we know that (1) the bound straddles the equator and (2) its
+ // width in longitude is at least Pi/2. In this case the minimum
+ // distance can occur either between a corner of B and the diagonally
+ // opposite corner of B' (as in the case above), or between a corner of B
+ // and the opposite longitudinal edge reflected in B'. It is sufficient
+ // to only consider the corner-edge case, since this distance is also a
+ // lower bound on the corner-corner distance when that case applies.
+
+ // Consider the spherical triangle XYZ where X is a corner of B with
+ // minimum absolute latitude, Y is the closest pole to X, and Z is the
+ // point closest to X on the opposite longitudinal edge of B'. This is a
+ // right triangle (Z = Pi/2), and from the spherical law of sines we have
+ //
+ // sin(z) / sin(Z) = sin(y) / sin(Y)
+ // sin(max_lat_gap) / 1 = sin(d_min) / sin(lng_gap)
+ // sin(d_min) = sin(max_lat_gap) * sin(lng_gap)
+ //
+ // where "max_lat_gap" = max(lat_gap1, lat_gap2) and "d_min" is the
+ // desired minimum distance. Now using the facts that sin(t) >= (2/Pi)*t
+ // for 0 <= t <= Pi/2, that we only need an accurate approximation when
+ // at least one of "max_lat_gap" or "lng_gap" is extremely small (in
+ // which case sin(t) ~= t), and recalling that "max_lat_gap" has an error
+ // of up to 0.75 * DBL_EPSILON, we want to test whether
+ //
+ // max_lat_gap * lng_gap < (4.309 + 0.75) * (Pi/2) * DBL_EPSILON
+ // ~= 1.765e-15
+ if (max(lat_gap1, lat_gap2) * lng_gap < 1.765e-15) {
+ return S2LatLngRect::Full();
+ }
+ }
+ // Next we need to check whether the subregion might contain any edges that
+ // span (M_PI - 2 * DBL_EPSILON) radians or more in longitude, since AddPoint
+ // sets the longitude bound to Full() in that case. This corresponds to
+ // testing whether (lng_gap <= 0) in "lng_expansion" below.
+
+ // Otherwise, the maximum latitude error in AddPoint is 4.8 * DBL_EPSILON.
+ // In the worst case, the errors when computing the latitude bound for a
+ // subregion could go in the opposite direction as the errors when computing
+ // the bound for the original region, so we need to double this value.
+ // (More analysis shows that it's okay to round down to a multiple of
+ // DBL_EPSILON.)
+ //
+ // For longitude, we rely on the fact that atan2 is correctly rounded and
+ // therefore no additional bounds expansion is necessary.
+
+ double lat_expansion = 9 * DBL_EPSILON;
+ double lng_expansion = (lng_gap <= 0) ? M_PI : 0;
+ return bound.Expanded(S2LatLng::FromRadians(lat_expansion,
+ lng_expansion)).PolarClosure();
+}
+
+S2LatLng S2LatLngRectBounder::MaxErrorForTests() {
+ // The maximum error in the latitude calculation is
+ // 3.84 * DBL_EPSILON for the RobustCrossProd calculation
+ // 0.96 * DBL_EPSILON for the Latitude() calculation
+ // 5 * DBL_EPSILON added by AddPoint/GetBound to compensate for error
+ // ------------------
+ // 9.80 * DBL_EPSILON maximum error in result
+ //
+ // The maximum error in the longitude calculation is DBL_EPSILON. GetBound
+ // does not do any expansion because this isn't necessary in order to
+ // bound the *rounded* longitudes of contained points.
+ return S2LatLng::FromRadians(10 * DBL_EPSILON, 1 * DBL_EPSILON);
+}
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2lax_loop_shape.h"
+
+#include <vector>
+
+#include "s2/s2loop.h"
+#include "s2/s2shapeutil_get_reference_point.h"
+
+using std::vector;
+using ReferencePoint = S2Shape::ReferencePoint;
+
+S2LaxLoopShape::S2LaxLoopShape(const vector<S2Point>& vertices) {
+ Init(vertices);
+}
+
+S2LaxLoopShape::S2LaxLoopShape(const S2Loop& loop) {
+ Init(loop);
+}
+
+void S2LaxLoopShape::Init(const vector<S2Point>& vertices) {
+ num_vertices_ = vertices.size();
+ vertices_.reset(new S2Point[num_vertices_]);
+ std::copy(vertices.begin(), vertices.end(), vertices_.get());
+}
+
+void S2LaxLoopShape::Init(const S2Loop& loop) {
+ S2_DCHECK(!loop.is_full()) << "Full loops not supported; use S2LaxPolygonShape";
+ if (loop.is_empty()) {
+ num_vertices_ = 0;
+ vertices_ = nullptr;
+ } else {
+ num_vertices_ = loop.num_vertices();
+ vertices_.reset(new S2Point[num_vertices_]);
+ std::copy(&loop.vertex(0), &loop.vertex(0) + num_vertices_,
+ vertices_.get());
+ }
+}
+
+S2Shape::Edge S2LaxLoopShape::edge(int e0) const {
+ S2_DCHECK_LT(e0, num_edges());
+ int e1 = e0 + 1;
+ if (e1 == num_vertices()) e1 = 0;
+ return Edge(vertices_[e0], vertices_[e1]);
+}
+
+S2Shape::Edge S2LaxLoopShape::chain_edge(int i, int j) const {
+ S2_DCHECK_EQ(i, 0);
+ S2_DCHECK_LT(j, num_edges());
+ int k = (j + 1 == num_vertices()) ? 0 : j + 1;
+ return Edge(vertices_[j], vertices_[k]);
+}
+
+S2Shape::ReferencePoint S2LaxLoopShape::GetReferencePoint() const {
+ return s2shapeutil::GetReferencePoint(*this);
+}
+
+S2VertexIdLaxLoopShape::S2VertexIdLaxLoopShape(
+ const std::vector<int32>& vertex_ids, const S2Point* vertex_array) {
+ Init(vertex_ids, vertex_array);
+}
+
+void S2VertexIdLaxLoopShape::Init(const std::vector<int32>& vertex_ids,
+ const S2Point* vertex_array) {
+ num_vertices_ = vertex_ids.size();
+ vertex_ids_.reset(new int32[num_vertices_]);
+ std::copy(vertex_ids.begin(), vertex_ids.end(), vertex_ids_.get());
+ vertex_array_ = vertex_array;
+}
+
+S2Shape::Edge S2VertexIdLaxLoopShape::edge(int e0) const {
+ S2_DCHECK_LT(e0, num_edges());
+ int e1 = e0 + 1;
+ if (e1 == num_vertices()) e1 = 0;
+ return Edge(vertex(e0), vertex(e1));
+}
+
+S2Shape::Edge S2VertexIdLaxLoopShape::chain_edge(int i, int j) const {
+ S2_DCHECK_EQ(i, 0);
+ S2_DCHECK_LT(j, num_edges());
+ int k = (j + 1 == num_vertices()) ? 0 : j + 1;
+ return Edge(vertex(j), vertex(k));
+}
+
+S2Shape::ReferencePoint S2VertexIdLaxLoopShape::GetReferencePoint() const {
+ // GetReferencePoint interprets a loop with no vertices as "full".
+ if (num_vertices() == 0) return ReferencePoint::Contained(false);
+ return s2shapeutil::GetReferencePoint(*this);
+}
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2lax_polygon_shape.h"
+
+#include "s2/s2shapeutil_get_reference_point.h"
+
+using absl::make_unique;
+using absl::MakeSpan;
+using absl::Span;
+using std::vector;
+using ChainPosition = S2Shape::ChainPosition;
+
+// When adding a new encoding, be aware that old binaries will not be able
+// to decode it.
+static const unsigned char kCurrentEncodingVersionNumber = 1;
+
+S2LaxPolygonShape::S2LaxPolygonShape(
+ const vector<S2LaxPolygonShape::Loop>& loops) {
+ Init(loops);
+}
+
+S2LaxPolygonShape::S2LaxPolygonShape(const S2Polygon& polygon) {
+ Init(polygon);
+}
+
+void S2LaxPolygonShape::Init(const vector<S2LaxPolygonShape::Loop>& loops) {
+ vector<Span<const S2Point>> spans;
+ spans.reserve(loops.size());
+ for (const S2LaxPolygonShape::Loop& loop : loops) {
+ spans.emplace_back(loop);
+ }
+ Init(spans);
+}
+
+void S2LaxPolygonShape::Init(const S2Polygon& polygon) {
+ vector<Span<const S2Point>> spans;
+ for (int i = 0; i < polygon.num_loops(); ++i) {
+ const S2Loop* loop = polygon.loop(i);
+ if (loop->is_full()) {
+ spans.emplace_back(); // Empty span.
+ } else {
+ spans.emplace_back(&loop->vertex(0), loop->num_vertices());
+ }
+ }
+ Init(spans);
+
+ // S2Polygon and S2LaxPolygonShape holes are oriented oppositely, so we need
+ // to reverse the orientation of any loops representing holes.
+ for (int i = 0; i < polygon.num_loops(); ++i) {
+ if (polygon.loop(i)->is_hole()) {
+ S2Point* v0 = &vertices_[cumulative_vertices_[i]];
+ std::reverse(v0, v0 + num_loop_vertices(i));
+ }
+ }
+}
+
+void S2LaxPolygonShape::Init(const vector<Span<const S2Point>>& loops) {
+ num_loops_ = loops.size();
+ if (num_loops_ == 0) {
+ num_vertices_ = 0;
+ vertices_ = nullptr;
+ } else if (num_loops_ == 1) {
+ num_vertices_ = loops[0].size();
+ vertices_.reset(new S2Point[num_vertices_]);
+ std::copy(loops[0].begin(), loops[0].end(), vertices_.get());
+ } else {
+ cumulative_vertices_ = new uint32[num_loops_ + 1];
+ int32 num_vertices = 0;
+ for (int i = 0; i < num_loops_; ++i) {
+ cumulative_vertices_[i] = num_vertices;
+ num_vertices += loops[i].size();
+ }
+ cumulative_vertices_[num_loops_] = num_vertices;
+ vertices_.reset(new S2Point[num_vertices]);
+ for (int i = 0; i < num_loops_; ++i) {
+ std::copy(loops[i].begin(), loops[i].end(),
+ vertices_.get() + cumulative_vertices_[i]);
+ }
+ }
+}
+
+S2LaxPolygonShape::~S2LaxPolygonShape() {
+ if (num_loops() > 1) {
+ delete[] cumulative_vertices_;
+ }
+}
+
+int S2LaxPolygonShape::num_vertices() const {
+ if (num_loops() <= 1) {
+ return num_vertices_;
+ } else {
+ return cumulative_vertices_[num_loops()];
+ }
+}
+
+int S2LaxPolygonShape::num_loop_vertices(int i) const {
+ S2_DCHECK_LT(i, num_loops());
+ if (num_loops() == 1) {
+ return num_vertices_;
+ } else {
+ return cumulative_vertices_[i + 1] - cumulative_vertices_[i];
+ }
+}
+
+const S2Point& S2LaxPolygonShape::loop_vertex(int i, int j) const {
+ S2_DCHECK_LT(i, num_loops());
+ S2_DCHECK_LT(j, num_loop_vertices(i));
+ if (num_loops() == 1) {
+ return vertices_[j];
+ } else {
+ return vertices_[cumulative_vertices_[i] + j];
+ }
+}
+
+void S2LaxPolygonShape::Encode(Encoder* encoder,
+ s2coding::CodingHint hint) const {
+ encoder->Ensure(1 + Varint::kMax32);
+ encoder->put8(kCurrentEncodingVersionNumber);
+ encoder->put_varint32(num_loops_);
+ s2coding::EncodeS2PointVector(MakeSpan(vertices_.get(), num_vertices()),
+ hint, encoder);
+ if (num_loops() > 1) {
+ s2coding::EncodeUintVector<uint32>(MakeSpan(cumulative_vertices_,
+ num_loops() + 1), encoder);
+ }
+}
+
+bool S2LaxPolygonShape::Init(Decoder* decoder) {
+ if (decoder->avail() < 1) return false;
+ uint8 version = decoder->get8();
+ if (version != kCurrentEncodingVersionNumber) return false;
+
+ uint32 num_loops;
+ if (!decoder->get_varint32(&num_loops)) return false;
+ num_loops_ = num_loops;
+ s2coding::EncodedS2PointVector vertices;
+ if (!vertices.Init(decoder)) return false;
+
+ if (num_loops_ == 0) {
+ num_vertices_ = 0;
+ vertices_ = nullptr;
+ } else {
+ vertices_ = make_unique<S2Point[]>(vertices.size());
+ for (int i = 0; i < vertices.size(); ++i) {
+ vertices_[i] = vertices[i];
+ }
+ if (num_loops_ == 1) {
+ num_vertices_ = vertices.size();
+ } else {
+ s2coding::EncodedUintVector<uint32> cumulative_vertices;
+ if (!cumulative_vertices.Init(decoder)) return false;
+ cumulative_vertices_ = new uint32[cumulative_vertices.size()];
+ for (int i = 0; i < cumulative_vertices.size(); ++i) {
+ cumulative_vertices_[i] = cumulative_vertices[i];
+ }
+ }
+ }
+ return true;
+}
+
+S2Shape::Edge S2LaxPolygonShape::edge(int e0) const {
+ S2_DCHECK_LT(e0, num_edges());
+ int e1 = e0 + 1;
+ if (num_loops() == 1) {
+ if (e1 == num_vertices_) { e1 = 0; }
+ } else {
+ // Find the index of the first vertex of the loop following this one.
+ const int kMaxLinearSearchLoops = 12; // From benchmarks.
+ uint32* next = cumulative_vertices_ + 1;
+ if (num_loops() <= kMaxLinearSearchLoops) {
+ while (*next <= e0) ++next;
+ } else {
+ next = std::lower_bound(next, next + num_loops(), e1);
+ }
+ // Wrap around to the first vertex of the loop if necessary.
+ if (e1 == *next) { e1 = next[-1]; }
+ }
+ return Edge(vertices_[e0], vertices_[e1]);
+}
+
+S2Shape::ReferencePoint S2LaxPolygonShape::GetReferencePoint() const {
+ return s2shapeutil::GetReferencePoint(*this);
+}
+
+S2Shape::Chain S2LaxPolygonShape::chain(int i) const {
+ S2_DCHECK_LT(i, num_loops());
+ if (num_loops() == 1) {
+ return Chain(0, num_vertices_);
+ } else {
+ int start = cumulative_vertices_[i];
+ return Chain(start, cumulative_vertices_[i + 1] - start);
+ }
+}
+
+S2Shape::Edge S2LaxPolygonShape::chain_edge(int i, int j) const {
+ S2_DCHECK_LT(i, num_loops());
+ S2_DCHECK_LT(j, num_loop_vertices(i));
+ int n = num_loop_vertices(i);
+ int k = (j + 1 == n) ? 0 : j + 1;
+ if (num_loops() == 1) {
+ return Edge(vertices_[j], vertices_[k]);
+ } else {
+ int base = cumulative_vertices_[i];
+ return Edge(vertices_[base + j], vertices_[base + k]);
+ }
+}
+
+S2Shape::ChainPosition S2LaxPolygonShape::chain_position(int e) const {
+ S2_DCHECK_LT(e, num_edges());
+ const int kMaxLinearSearchLoops = 12; // From benchmarks.
+ if (num_loops() == 1) {
+ return ChainPosition(0, e);
+ } else {
+ // Find the index of the first vertex of the loop following this one.
+ uint32* next = cumulative_vertices_ + 1;
+ if (num_loops() <= kMaxLinearSearchLoops) {
+ while (*next <= e) ++next;
+ } else {
+ next = std::lower_bound(next, next + num_loops(), e + 1);
+ }
+ return ChainPosition(next - (cumulative_vertices_ + 1), e - next[-1]);
+ }
+}
+
+bool EncodedS2LaxPolygonShape::Init(Decoder* decoder) {
+ if (decoder->avail() < 1) return false;
+ uint8 version = decoder->get8();
+ if (version != kCurrentEncodingVersionNumber) return false;
+
+ uint32 num_loops;
+ if (!decoder->get_varint32(&num_loops)) return false;
+ num_loops_ = num_loops;
+
+ if (!vertices_.Init(decoder)) return false;
+
+ if (num_loops_ > 1) {
+ if (!cumulative_vertices_.Init(decoder)) return false;
+ }
+ return true;
+}
+
+int EncodedS2LaxPolygonShape::num_vertices() const {
+ if (num_loops() <= 1) {
+ return vertices_.size();
+ } else {
+ return cumulative_vertices_[num_loops()];
+ }
+}
+
+int EncodedS2LaxPolygonShape::num_loop_vertices(int i) const {
+ S2_DCHECK_LT(i, num_loops());
+ if (num_loops() == 1) {
+ return vertices_.size();
+ } else {
+ return cumulative_vertices_[i + 1] - cumulative_vertices_[i];
+ }
+}
+
+S2Point EncodedS2LaxPolygonShape::loop_vertex(int i, int j) const {
+ S2_DCHECK_LT(i, num_loops());
+ S2_DCHECK_LT(j, num_loop_vertices(i));
+ if (num_loops() == 1) {
+ return vertices_[j];
+ } else {
+ return vertices_[cumulative_vertices_[i] + j];
+ }
+}
+
+S2Shape::Edge EncodedS2LaxPolygonShape::edge(int e) const {
+ S2_DCHECK_LT(e, num_edges());
+ int e1 = e + 1;
+ if (num_loops() == 1) {
+ if (e1 == vertices_.size()) { e1 = 0; }
+ } else {
+ // Find the index of the first vertex of the loop following this one.
+ const int kMaxLinearSearchLoops = 12; // From benchmarks.
+ int next = 1;
+ if (num_loops() <= kMaxLinearSearchLoops) {
+ while (cumulative_vertices_[next] <= e) ++next;
+ } else {
+ next = cumulative_vertices_.lower_bound(e1);
+ }
+ // Wrap around to the first vertex of the loop if necessary.
+ if (e1 == cumulative_vertices_[next]) {
+ e1 = cumulative_vertices_[next - 1];
+ }
+ }
+ return Edge(vertices_[e], vertices_[e1]);
+}
+
+S2Shape::ReferencePoint EncodedS2LaxPolygonShape::GetReferencePoint() const {
+ return s2shapeutil::GetReferencePoint(*this);
+}
+
+S2Shape::Chain EncodedS2LaxPolygonShape::chain(int i) const {
+ S2_DCHECK_LT(i, num_loops());
+ if (num_loops() == 1) {
+ return Chain(0, vertices_.size());
+ } else {
+ int start = cumulative_vertices_[i];
+ return Chain(start, cumulative_vertices_[i + 1] - start);
+ }
+}
+
+S2Shape::Edge EncodedS2LaxPolygonShape::chain_edge(int i, int j) const {
+ S2_DCHECK_LT(i, num_loops());
+ S2_DCHECK_LT(j, num_loop_vertices(i));
+ int n = num_loop_vertices(i);
+ int k = (j + 1 == n) ? 0 : j + 1;
+ if (num_loops() == 1) {
+ return Edge(vertices_[j], vertices_[k]);
+ } else {
+ int base = cumulative_vertices_[i];
+ return Edge(vertices_[base + j], vertices_[base + k]);
+ }
+}
+
+S2Shape::ChainPosition EncodedS2LaxPolygonShape::chain_position(int e) const {
+ S2_DCHECK_LT(e, num_edges());
+ const int kMaxLinearSearchLoops = 12; // From benchmarks.
+ if (num_loops() == 1) {
+ return ChainPosition(0, e);
+ } else {
+ // Find the index of the first vertex of the loop following this one.
+ int next = 1;
+ if (num_loops() <= kMaxLinearSearchLoops) {
+ while (cumulative_vertices_[next] <= e) ++next;
+ } else {
+ next = cumulative_vertices_.lower_bound(e + 1);
+ }
+ return ChainPosition(next - 1, e - cumulative_vertices_[next - 1]);
+ }
+}
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2lax_polyline_shape.h"
+
+#include <algorithm>
+#include "s2/base/logging.h"
+#include "s2/s2polyline.h"
+
+using absl::make_unique;
+using absl::MakeSpan;
+using std::vector;
+
+S2LaxPolylineShape::S2LaxPolylineShape(const vector<S2Point>& vertices) {
+ Init(vertices);
+}
+
+S2LaxPolylineShape::S2LaxPolylineShape(const S2Polyline& polyline) {
+ Init(polyline);
+}
+
+void S2LaxPolylineShape::Init(const vector<S2Point>& vertices) {
+ num_vertices_ = vertices.size();
+ S2_LOG_IF(WARNING, num_vertices_ == 1)
+ << "s2shapeutil::S2LaxPolylineShape with one vertex has no edges";
+ vertices_.reset(new S2Point[num_vertices_]);
+ std::copy(vertices.begin(), vertices.end(), vertices_.get());
+}
+
+void S2LaxPolylineShape::Init(const S2Polyline& polyline) {
+ num_vertices_ = polyline.num_vertices();
+ S2_LOG_IF(WARNING, num_vertices_ == 1)
+ << "s2shapeutil::S2LaxPolylineShape with one vertex has no edges";
+ vertices_.reset(new S2Point[num_vertices_]);
+ std::copy(&polyline.vertex(0), &polyline.vertex(0) + num_vertices_,
+ vertices_.get());
+}
+
+void S2LaxPolylineShape::Encode(Encoder* encoder,
+ s2coding::CodingHint hint) const {
+ s2coding::EncodeS2PointVector(MakeSpan(vertices_.get(), num_vertices_),
+ hint, encoder);
+}
+
+bool S2LaxPolylineShape::Init(Decoder* decoder) {
+ s2coding::EncodedS2PointVector vertices;
+ if (!vertices.Init(decoder)) return false;
+ num_vertices_ = vertices.size();
+ vertices_ = make_unique<S2Point[]>(vertices.size());
+ for (int i = 0; i < num_vertices_; ++i) {
+ vertices_[i] = vertices[i];
+ }
+ return true;
+}
+
+S2Shape::Edge S2LaxPolylineShape::edge(int e) const {
+ S2_DCHECK_LT(e, num_edges());
+ return Edge(vertex(e), vertex(e + 1));
+}
+
+int S2LaxPolylineShape::num_chains() const {
+ return std::min(1, S2LaxPolylineShape::num_edges()); // Avoid virtual call.
+}
+
+S2Shape::Chain S2LaxPolylineShape::chain(int i) const {
+ return Chain(0, S2LaxPolylineShape::num_edges()); // Avoid virtual call.
+}
+
+S2Shape::Edge S2LaxPolylineShape::chain_edge(int i, int j) const {
+ S2_DCHECK_EQ(i, 0);
+ S2_DCHECK_LT(j, num_edges());
+ return Edge(vertex(j), vertex(j + 1));
+}
+
+S2Shape::ChainPosition S2LaxPolylineShape::chain_position(int e) const {
+ return S2Shape::ChainPosition(0, e);
+}
+
+bool EncodedS2LaxPolylineShape::Init(Decoder* decoder) {
+ return vertices_.Init(decoder);
+}
+
+S2Shape::Edge EncodedS2LaxPolylineShape::edge(int e) const {
+ S2_DCHECK_LT(e, num_edges());
+ return Edge(vertex(e), vertex(e + 1));
+}
+
+int EncodedS2LaxPolylineShape::num_chains() const {
+ return std::min(1, EncodedS2LaxPolylineShape::num_edges());
+}
+
+S2Shape::Chain EncodedS2LaxPolylineShape::chain(int i) const {
+ return Chain(0, EncodedS2LaxPolylineShape::num_edges());
+}
+
+S2Shape::Edge EncodedS2LaxPolylineShape::chain_edge(int i, int j) const {
+ S2_DCHECK_EQ(i, 0);
+ S2_DCHECK_LT(j, num_edges());
+ return Edge(vertex(j), vertex(j + 1));
+}
+
+S2Shape::ChainPosition EncodedS2LaxPolylineShape::chain_position(int e) const {
+ return S2Shape::ChainPosition(0, e);
+}
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2loop.h"
+
+#include <algorithm>
+#include <atomic>
+#include <bitset>
+#include <cfloat>
+#include <cmath>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "s2/base/commandlineflags.h"
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+#include "s2/mutable_s2shape_index.h"
+#include "s2/r1interval.h"
+#include "s2/s1angle.h"
+#include "s2/s1interval.h"
+#include "s2/s2cap.h"
+#include "s2/s2cell.h"
+#include "s2/s2centroids.h"
+#include "s2/s2closest_edge_query.h"
+#include "s2/s2coords.h"
+#include "s2/s2crossing_edge_query.h"
+#include "s2/s2debug.h"
+#include "s2/s2edge_clipping.h"
+#include "s2/s2edge_crosser.h"
+#include "s2/s2edge_distances.h"
+#include "s2/s2error.h"
+#include "s2/s2latlng_rect_bounder.h"
+#include "s2/s2measures.h"
+#include "s2/s2padded_cell.h"
+#include "s2/s2point_compression.h"
+#include "s2/s2pointutil.h"
+#include "s2/s2predicates.h"
+#include "s2/s2shape_index.h"
+#include "s2/s2shapeutil_visit_crossing_edge_pairs.h"
+#include "s2/s2wedge_relations.h"
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/third_party/absl/types/span.h"
+#include "s2/util/coding/coder.h"
+#include "s2/util/coding/coder.h"
+#include "s2/util/math/matrix3x3.h"
+
+using absl::make_unique;
+using absl::MakeSpan;
+using std::pair;
+using std::set;
+using std::vector;
+
+DEFINE_bool(
+ s2loop_lazy_indexing, true,
+ "Build the S2ShapeIndex only when it is first needed. This can save "
+ "significant amounts of memory and time when geometry is constructed but "
+ "never queried, for example when loops are passed directly to S2Polygon, "
+ "or when geometry is being converted from one format to another.");
+
+// The maximum number of vertices we'll allow when decoding a loop.
+// The default value of 50 million is about 30x bigger than the number of
+DEFINE_int32(
+ s2polygon_decode_max_num_vertices, 50000000,
+ "The upper limit on the number of loops that are allowed by the "
+ "S2Polygon::Decode method.");
+
+static const unsigned char kCurrentLosslessEncodingVersionNumber = 1;
+
+// Boolean properties for compressed loops.
+// See GetCompressedEncodingProperties.
+enum CompressedLoopProperty {
+ kOriginInside,
+ kBoundEncoded,
+ kNumProperties
+};
+
+S2Loop::S2Loop() {
+ // The loop is not valid until Init() is called.
+}
+
+S2Loop::S2Loop(const vector<S2Point>& vertices)
+ : S2Loop(vertices, S2Debug::ALLOW) {}
+
+S2Loop::S2Loop(const vector<S2Point>& vertices,
+ S2Debug override)
+ : s2debug_override_(override) {
+ Init(vertices);
+}
+
+void S2Loop::set_s2debug_override(S2Debug override) {
+ s2debug_override_ = override;
+}
+
+S2Debug S2Loop::s2debug_override() const {
+ return s2debug_override_;
+}
+
+void S2Loop::ClearIndex() {
+ unindexed_contains_calls_.store(0, std::memory_order_relaxed);
+ index_.Clear();
+}
+
+void S2Loop::Init(const vector<S2Point>& vertices) {
+ ClearIndex();
+ if (owns_vertices_) delete[] vertices_;
+ num_vertices_ = vertices.size();
+ vertices_ = new S2Point[num_vertices_];
+ std::copy(vertices.begin(), vertices.end(), &vertices_[0]);
+ owns_vertices_ = true;
+ InitOriginAndBound();
+}
+
+bool S2Loop::IsValid() const {
+ S2Error error;
+ if (FindValidationError(&error)) {
+ S2_LOG_IF(ERROR, FLAGS_s2debug) << error;
+ return false;
+ }
+ return true;
+}
+
+bool S2Loop::FindValidationError(S2Error* error) const {
+ return (FindValidationErrorNoIndex(error) ||
+ s2shapeutil::FindSelfIntersection(index_, error));
+}
+
+bool S2Loop::FindValidationErrorNoIndex(S2Error* error) const {
+ // subregion_bound_ must be at least as large as bound_. (This is an
+ // internal consistency check rather than a test of client data.)
+ S2_DCHECK(subregion_bound_.Contains(bound_));
+
+ // All vertices must be unit length. (Unfortunately this check happens too
+ // late in debug mode, because S2Loop construction calls s2pred::Sign which
+ // expects vertices to be unit length. But it is still a useful check in
+ // optimized builds.)
+ for (int i = 0; i < num_vertices(); ++i) {
+ if (!S2::IsUnitLength(vertex(i))) {
+ error->Init(S2Error::NOT_UNIT_LENGTH,
+ "Vertex %d is not unit length", i);
+ return true;
+ }
+ }
+ // Loops must have at least 3 vertices (except for the empty and full loops).
+ if (num_vertices() < 3) {
+ if (is_empty_or_full()) {
+ return false; // Skip remaining tests.
+ }
+ error->Init(S2Error::LOOP_NOT_ENOUGH_VERTICES,
+ "Non-empty, non-full loops must have at least 3 vertices");
+ return true;
+ }
+ // Loops are not allowed to have any duplicate vertices or edge crossings.
+ // We split this check into two parts. First we check that no edge is
+ // degenerate (identical endpoints). Then we check that there are no
+ // intersections between non-adjacent edges (including at vertices). The
+ // second part needs the S2ShapeIndex, so it does not fall within the scope
+ // of this method.
+ for (int i = 0; i < num_vertices(); ++i) {
+ if (vertex(i) == vertex(i+1)) {
+ error->Init(S2Error::DUPLICATE_VERTICES,
+ "Edge %d is degenerate (duplicate vertex)", i);
+ return true;
+ }
+ if (vertex(i) == -vertex(i + 1)) {
+ error->Init(S2Error::ANTIPODAL_VERTICES,
+ "Vertices %d and %d are antipodal", i,
+ (i + 1) % num_vertices());
+ return true;
+ }
+ }
+ return false;
+}
+
+void S2Loop::InitOriginAndBound() {
+ if (num_vertices() < 3) {
+ // Check for the special empty and full loops (which have one vertex).
+ if (!is_empty_or_full()) {
+ origin_inside_ = false;
+ return; // Bail out without trying to access non-existent vertices.
+ }
+ // If the vertex is in the southern hemisphere then the loop is full,
+ // otherwise it is empty.
+ origin_inside_ = (vertex(0).z() < 0);
+ } else {
+ // Point containment testing is done by counting edge crossings starting
+ // at a fixed point on the sphere (S2::Origin()). Historically this was
+ // important, but it is now no longer necessary, and it may be worthwhile
+ // experimenting with using a loop vertex as the reference point. In any
+ // case, we need to know whether the reference point (S2::Origin) is
+ // inside or outside the loop before we can construct the S2ShapeIndex.
+ // We do this by first guessing that it is outside, and then seeing
+ // whether we get the correct containment result for vertex 1. If the
+ // result is incorrect, the origin must be inside the loop.
+ //
+ // A loop with consecutive vertices A,B,C contains vertex B if and only if
+ // the fixed vector R = S2::Ortho(B) is contained by the wedge ABC. The
+ // wedge is closed at A and open at C, i.e. the point B is inside the loop
+ // if A=R but not if C=R. This convention is required for compatibility
+ // with S2::VertexCrossing. (Note that we can't use S2::Origin()
+ // as the fixed vector because of the possibility that B == S2::Origin().)
+ //
+ // TODO(ericv): Investigate using vertex(0) as the reference point.
+
+ origin_inside_ = false; // Initialize before calling Contains().
+ bool v1_inside = s2pred::OrderedCCW(S2::Ortho(vertex(1)), vertex(0),
+ vertex(2), vertex(1));
+ // Note that Contains(S2Point) only does a bounds check once InitIndex()
+ // has been called, so it doesn't matter that bound_ is undefined here.
+ if (v1_inside != Contains(vertex(1))) {
+ origin_inside_ = true;
+ }
+ }
+ // We *must* call InitBound() before InitIndex(), because InitBound() calls
+ // Contains(S2Point), and Contains(S2Point) does a bounds check whenever the
+ // index is not fresh (i.e., the loop has been added to the index but the
+ // index has not been updated yet).
+ //
+ // TODO(ericv): When fewer S2Loop methods depend on internal bounds checks,
+ // consider computing the bound on demand as well.
+ InitBound();
+ InitIndex();
+}
+
+void S2Loop::InitBound() {
+ // Check for the special empty and full loops.
+ if (is_empty_or_full()) {
+ if (is_empty()) {
+ subregion_bound_ = bound_ = S2LatLngRect::Empty();
+ } else {
+ subregion_bound_ = bound_ = S2LatLngRect::Full();
+ }
+ return;
+ }
+
+ // The bounding rectangle of a loop is not necessarily the same as the
+ // bounding rectangle of its vertices. First, the maximal latitude may be
+ // attained along the interior of an edge. Second, the loop may wrap
+ // entirely around the sphere (e.g. a loop that defines two revolutions of a
+ // candy-cane stripe). Third, the loop may include one or both poles.
+ // Note that a small clockwise loop near the equator contains both poles.
+
+ S2LatLngRectBounder bounder;
+ for (int i = 0; i <= num_vertices(); ++i) {
+ bounder.AddPoint(vertex(i));
+ }
+ S2LatLngRect b = bounder.GetBound();
+ if (Contains(S2Point(0, 0, 1))) {
+ b = S2LatLngRect(R1Interval(b.lat().lo(), M_PI_2), S1Interval::Full());
+ }
+ // If a loop contains the south pole, then either it wraps entirely
+ // around the sphere (full longitude range), or it also contains the
+ // north pole in which case b.lng().is_full() due to the test above.
+ // Either way, we only need to do the south pole containment test if
+ // b.lng().is_full().
+ if (b.lng().is_full() && Contains(S2Point(0, 0, -1))) {
+ b.mutable_lat()->set_lo(-M_PI_2);
+ }
+ bound_ = b;
+ subregion_bound_ = S2LatLngRectBounder::ExpandForSubregions(bound_);
+}
+
+void S2Loop::InitIndex() {
+ index_.Add(make_unique<Shape>(this));
+ if (!FLAGS_s2loop_lazy_indexing) {
+ index_.ForceBuild();
+ }
+ if (FLAGS_s2debug && s2debug_override_ == S2Debug::ALLOW) {
+ // Note that FLAGS_s2debug is false in optimized builds (by default).
+ S2_CHECK(IsValid());
+ }
+}
+
+S2Loop::S2Loop(const S2Cell& cell)
+ : depth_(0),
+ num_vertices_(4),
+ vertices_(new S2Point[num_vertices_]),
+ owns_vertices_(true),
+ s2debug_override_(S2Debug::ALLOW),
+ unindexed_contains_calls_(0) {
+ for (int i = 0; i < 4; ++i) {
+ vertices_[i] = cell.GetVertex(i);
+ }
+ // We recompute the bounding rectangle ourselves, since S2Cell uses a
+ // different method and we need all the bounds to be consistent.
+ InitOriginAndBound();
+}
+
+S2Loop::~S2Loop() {
+ if (owns_vertices_) delete[] vertices_;
+}
+
+S2Loop::S2Loop(const S2Loop& src)
+ : depth_(src.depth_),
+ num_vertices_(src.num_vertices_),
+ vertices_(new S2Point[num_vertices_]),
+ owns_vertices_(true),
+ s2debug_override_(src.s2debug_override_),
+ origin_inside_(src.origin_inside_),
+ unindexed_contains_calls_(0),
+ bound_(src.bound_),
+ subregion_bound_(src.subregion_bound_) {
+ std::copy(&src.vertices_[0], &src.vertices_[num_vertices_], &vertices_[0]);
+ InitIndex();
+}
+
+S2Loop* S2Loop::Clone() const {
+ return new S2Loop(*this);
+}
+
+int S2Loop::FindVertex(const S2Point& p) const {
+ if (num_vertices() < 10) {
+ // Exhaustive search. Return value must be in the range [1..N].
+ for (int i = 1; i <= num_vertices(); ++i) {
+ if (vertex(i) == p) return i;
+ }
+ return -1;
+ }
+ MutableS2ShapeIndex::Iterator it(&index_);
+ if (!it.Locate(p)) return -1;
+
+ const S2ClippedShape& a_clipped = it.cell().clipped(0);
+ for (int i = a_clipped.num_edges() - 1; i >= 0; --i) {
+ int ai = a_clipped.edge(i);
+ // Return value must be in the range [1..N].
+ if (vertex(ai) == p) return (ai == 0) ? num_vertices() : ai;
+ if (vertex(ai+1) == p) return ai+1;
+ }
+ return -1;
+}
+
+bool S2Loop::IsNormalized() const {
+ // Optimization: if the longitude span is less than 180 degrees, then the
+ // loop covers less than half the sphere and is therefore normalized.
+ if (bound_.lng().GetLength() < M_PI) return true;
+
+ return S2::IsNormalized(vertices_span());
+}
+
+void S2Loop::Normalize() {
+ S2_CHECK(owns_vertices_);
+ if (!IsNormalized()) Invert();
+ S2_DCHECK(IsNormalized());
+}
+
+void S2Loop::Invert() {
+ S2_CHECK(owns_vertices_);
+ ClearIndex();
+ if (is_empty_or_full()) {
+ vertices_[0] = is_full() ? kEmptyVertex() : kFullVertex();
+ } else {
+ std::reverse(vertices_, vertices_ + num_vertices());
+ }
+ // origin_inside_ must be set correctly before building the S2ShapeIndex.
+ origin_inside_ ^= true;
+ if (bound_.lat().lo() > -M_PI_2 && bound_.lat().hi() < M_PI_2) {
+ // The complement of this loop contains both poles.
+ subregion_bound_ = bound_ = S2LatLngRect::Full();
+ } else {
+ InitBound();
+ }
+ InitIndex();
+}
+
+double S2Loop::GetArea() const {
+ // S2Loop has its own convention for empty and full loops.
+ if (is_empty_or_full()) {
+ return contains_origin() ? (4 * M_PI) : 0;
+ }
+ return S2::GetArea(vertices_span());
+}
+
+S2Point S2Loop::GetCentroid() const {
+ // Empty and full loops are handled correctly.
+ return S2::GetCentroid(vertices_span());
+}
+
+S2::LoopOrder S2Loop::GetCanonicalLoopOrder() const {
+ return S2::GetCanonicalLoopOrder(vertices_span());
+}
+
+S1Angle S2Loop::GetDistance(const S2Point& x) const {
+ // Note that S2Loop::Contains(S2Point) is slightly more efficient than the
+ // generic version used by S2ClosestEdgeQuery.
+ if (Contains(x)) return S1Angle::Zero();
+ return GetDistanceToBoundary(x);
+}
+
+S1Angle S2Loop::GetDistanceToBoundary(const S2Point& x) const {
+ S2ClosestEdgeQuery::Options options;
+ options.set_include_interiors(false);
+ S2ClosestEdgeQuery::PointTarget t(x);
+ return S2ClosestEdgeQuery(&index_, options).GetDistance(&t).ToAngle();
+}
+
+S2Point S2Loop::Project(const S2Point& x) const {
+ if (Contains(x)) return x;
+ return ProjectToBoundary(x);
+}
+
+S2Point S2Loop::ProjectToBoundary(const S2Point& x) const {
+ S2ClosestEdgeQuery::Options options;
+ options.set_include_interiors(false);
+ S2ClosestEdgeQuery q(&index_, options);
+ S2ClosestEdgeQuery::PointTarget target(x);
+ S2ClosestEdgeQuery::Result edge = q.FindClosestEdge(&target);
+ return q.Project(x, edge);
+}
+
+double S2Loop::GetCurvature() const {
+ // S2Loop has its own convention for empty and full loops. For such loops,
+ // we return the limit value as the area approaches 0 or 4*Pi respectively.
+ if (is_empty_or_full()) {
+ return contains_origin() ? (-2 * M_PI) : (2 * M_PI);
+ }
+ return S2::GetCurvature(vertices_span());
+}
+
+double S2Loop::GetCurvatureMaxError() const {
+ return S2::GetCurvatureMaxError(vertices_span());
+}
+
+S2Cap S2Loop::GetCapBound() const {
+ return bound_.GetCapBound();
+}
+
+bool S2Loop::Contains(const S2Cell& target) const {
+ MutableS2ShapeIndex::Iterator it(&index_);
+ S2ShapeIndex::CellRelation relation = it.Locate(target.id());
+
+ // If "target" is disjoint from all index cells, it is not contained.
+ // Similarly, if "target" is subdivided into one or more index cells then it
+ // is not contained, since index cells are subdivided only if they (nearly)
+ // intersect a sufficient number of edges. (But note that if "target" itself
+ // is an index cell then it may be contained, since it could be a cell with
+ // no edges in the loop interior.)
+ if (relation != S2ShapeIndex::INDEXED) return false;
+
+ // Otherwise check if any edges intersect "target".
+ if (BoundaryApproxIntersects(it, target)) return false;
+
+ // Otherwise check if the loop contains the center of "target".
+ return Contains(it, target.GetCenter());
+}
+
+bool S2Loop::MayIntersect(const S2Cell& target) const {
+ MutableS2ShapeIndex::Iterator it(&index_);
+ S2ShapeIndex::CellRelation relation = it.Locate(target.id());
+
+ // If "target" does not overlap any index cell, there is no intersection.
+ if (relation == S2ShapeIndex::DISJOINT) return false;
+
+ // If "target" is subdivided into one or more index cells, there is an
+ // intersection to within the S2ShapeIndex error bound (see Contains).
+ if (relation == S2ShapeIndex::SUBDIVIDED) return true;
+
+ // If "target" is an index cell, there is an intersection because index cells
+ // are created only if they have at least one edge or they are entirely
+ // contained by the loop.
+ if (it.id() == target.id()) return true;
+
+ // Otherwise check if any edges intersect "target".
+ if (BoundaryApproxIntersects(it, target)) return true;
+
+ // Otherwise check if the loop contains the center of "target".
+ return Contains(it, target.GetCenter());
+}
+
+bool S2Loop::BoundaryApproxIntersects(const MutableS2ShapeIndex::Iterator& it,
+ const S2Cell& target) const {
+ S2_DCHECK(it.id().contains(target.id()));
+ const S2ClippedShape& a_clipped = it.cell().clipped(0);
+ int a_num_edges = a_clipped.num_edges();
+
+ // If there are no edges, there is no intersection.
+ if (a_num_edges == 0) return false;
+
+ // We can save some work if "target" is the index cell itself.
+ if (it.id() == target.id()) return true;
+
+ // Otherwise check whether any of the edges intersect "target".
+ static const double kMaxError = (S2::kFaceClipErrorUVCoord +
+ S2::kIntersectsRectErrorUVDist);
+ R2Rect bound = target.GetBoundUV().Expanded(kMaxError);
+ for (int i = 0; i < a_num_edges; ++i) {
+ int ai = a_clipped.edge(i);
+ R2Point v0, v1;
+ if (S2::ClipToPaddedFace(vertex(ai), vertex(ai+1), target.face(),
+ kMaxError, &v0, &v1) &&
+ S2::IntersectsRect(v0, v1, bound)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool S2Loop::Contains(const S2Point& p) const {
+ // NOTE(ericv): A bounds check slows down this function by about 50%. It is
+ // worthwhile only when it might allow us to delay building the index.
+ if (!index_.is_fresh() && !bound_.Contains(p)) return false;
+
+ // For small loops it is faster to just check all the crossings. We also
+ // use this method during loop initialization because InitOriginAndBound()
+ // calls Contains() before InitIndex(). Otherwise, we keep track of the
+ // number of calls to Contains() and only build the index when enough calls
+ // have been made so that we think it is worth the effort. Note that the
+ // code below is structured so that if many calls are made in parallel only
+ // one thread builds the index, while the rest continue using brute force
+ // until the index is actually available.
+ //
+ // The constants below were tuned using the benchmarks. It turns out that
+ // building the index costs roughly 50x as much as Contains(). (The ratio
+ // increases slowly from 46x with 64 points to 61x with 256k points.) The
+ // textbook approach to this problem would be to wait until the cumulative
+ // time we would have saved with an index approximately equals the cost of
+ // building the index, and then build it. (This gives the optimal
+ // competitive ratio of 2; look up "competitive algorithms" for details.)
+ // We set the limit somewhat lower than this (20 rather than 50) because
+ // building the index may be forced anyway by other API calls, and so we
+ // want to err on the side of building it too early.
+
+ static const int kMaxBruteForceVertices = 32;
+ static const int kMaxUnindexedContainsCalls = 20; // See notes above.
+ if (index_.num_shape_ids() == 0 || // InitIndex() not called yet
+ num_vertices() <= kMaxBruteForceVertices ||
+ (!index_.is_fresh() &&
+ ++unindexed_contains_calls_ != kMaxUnindexedContainsCalls)) {
+ return BruteForceContains(p);
+ }
+ // Otherwise we look up the S2ShapeIndex cell containing this point. Note
+ // the index is built automatically the first time an iterator is created.
+ MutableS2ShapeIndex::Iterator it(&index_);
+ if (!it.Locate(p)) return false;
+ return Contains(it, p);
+}
+
+bool S2Loop::BruteForceContains(const S2Point& p) const {
+ // Empty and full loops don't need a special case, but invalid loops with
+ // zero vertices do, so we might as well handle them all at once.
+ if (num_vertices() < 3) return origin_inside_;
+
+ S2Point origin = S2::Origin();
+ S2EdgeCrosser crosser(&origin, &p, &vertex(0));
+ bool inside = origin_inside_;
+ for (int i = 1; i <= num_vertices(); ++i) {
+ inside ^= crosser.EdgeOrVertexCrossing(&vertex(i));
+ }
+ return inside;
+}
+
+bool S2Loop::Contains(const MutableS2ShapeIndex::Iterator& it,
+ const S2Point& p) const {
+ // Test containment by drawing a line segment from the cell center to the
+ // given point and counting edge crossings.
+ const S2ClippedShape& a_clipped = it.cell().clipped(0);
+ bool inside = a_clipped.contains_center();
+ int a_num_edges = a_clipped.num_edges();
+ if (a_num_edges > 0) {
+ S2Point center = it.center();
+ S2EdgeCrosser crosser(¢er, &p);
+ int ai_prev = -2;
+ for (int i = 0; i < a_num_edges; ++i) {
+ int ai = a_clipped.edge(i);
+ if (ai != ai_prev + 1) crosser.RestartAt(&vertex(ai));
+ ai_prev = ai;
+ inside ^= crosser.EdgeOrVertexCrossing(&vertex(ai+1));
+ }
+ }
+ return inside;
+}
+
+void S2Loop::Encode(Encoder* const encoder) const {
+ encoder->Ensure(num_vertices_ * sizeof(*vertices_) + 20); // sufficient
+
+ encoder->put8(kCurrentLosslessEncodingVersionNumber);
+ encoder->put32(num_vertices_);
+ encoder->putn(vertices_, sizeof(*vertices_) * num_vertices_);
+ encoder->put8(origin_inside_);
+ encoder->put32(depth_);
+ S2_DCHECK_GE(encoder->avail(), 0);
+
+ bound_.Encode(encoder);
+}
+
+bool S2Loop::Decode(Decoder* const decoder) {
+ if (decoder->avail() < sizeof(unsigned char)) return false;
+ unsigned char version = decoder->get8();
+ switch (version) {
+ case kCurrentLosslessEncodingVersionNumber:
+ return DecodeInternal(decoder, false);
+ }
+ return false;
+}
+
+bool S2Loop::DecodeWithinScope(Decoder* const decoder) {
+ if (decoder->avail() < sizeof(unsigned char)) return false;
+ unsigned char version = decoder->get8();
+ switch (version) {
+ case kCurrentLosslessEncodingVersionNumber:
+ return DecodeInternal(decoder, true);
+ }
+ return false;
+}
+
+bool S2Loop::DecodeInternal(Decoder* const decoder,
+ bool within_scope) {
+ // Perform all checks before modifying vertex state. Empty loops are
+ // explicitly allowed here: a newly created loop has zero vertices
+ // and such loops encode and decode properly.
+ if (decoder->avail() < sizeof(uint32)) return false;
+ const uint32 num_vertices = decoder->get32();
+ if (num_vertices > FLAGS_s2polygon_decode_max_num_vertices) {
+ return false;
+ }
+ if (decoder->avail() < (num_vertices * sizeof(*vertices_) +
+ sizeof(uint8) + sizeof(uint32))) {
+ return false;
+ }
+ ClearIndex();
+ if (owns_vertices_) delete[] vertices_;
+ num_vertices_ = num_vertices;
+
+ // x86 can do unaligned floating-point reads; however, many other
+ // platforms cannot. Do not use the zero-copy version if we are on
+ // an architecture that does not support unaligned reads, and the
+ // pointer is not correctly aligned.
+#if defined(__x86_64__) || defined(_M_X64) || defined(__i386) || \
+ defined(_M_IX86)
+ bool is_misaligned = false;
+#else
+ bool is_misaligned =
+ reinterpret_cast<intptr_t>(decoder->ptr()) % sizeof(double) != 0;
+#endif
+ if (within_scope && !is_misaligned) {
+ vertices_ = const_cast<S2Point *>(reinterpret_cast<const S2Point*>(
+ decoder->ptr()));
+ decoder->skip(num_vertices_ * sizeof(*vertices_));
+ owns_vertices_ = false;
+ } else {
+ vertices_ = new S2Point[num_vertices_];
+ decoder->getn(vertices_, num_vertices_ * sizeof(*vertices_));
+ owns_vertices_ = true;
+ }
+ origin_inside_ = decoder->get8();
+ depth_ = decoder->get32();
+ if (!bound_.Decode(decoder)) return false;
+ subregion_bound_ = S2LatLngRectBounder::ExpandForSubregions(bound_);
+
+ // An initialized loop will have some non-zero count of vertices. A default
+ // (uninitialized) has zero vertices. This code supports encoding and
+ // decoding of uninitialized loops, but we only want to call InitIndex for
+ // initialized loops. Otherwise we defer InitIndex until the call to Init().
+ if (num_vertices > 0) {
+ InitIndex();
+ }
+
+ return true;
+}
+
+// LoopRelation is an abstract class that defines a relationship between two
+// loops (Contains, Intersects, or CompareBoundary).
+class LoopRelation {
+ public:
+ LoopRelation() {}
+ virtual ~LoopRelation() {}
+
+ // Optionally, a_target() and b_target() can specify an early-exit condition
+ // for the loop relation. If any point P is found such that
+ //
+ // A.Contains(P) == a_crossing_target() &&
+ // B.Contains(P) == b_crossing_target()
+ //
+ // then the loop relation is assumed to be the same as if a pair of crossing
+ // edges were found. For example, the Contains() relation has
+ //
+ // a_crossing_target() == 0
+ // b_crossing_target() == 1
+ //
+ // because if A.Contains(P) == 0 (false) and B.Contains(P) == 1 (true) for
+ // any point P, then it is equivalent to finding an edge crossing (i.e.,
+ // since Contains() returns false in both cases).
+ //
+ // Loop relations that do not have an early-exit condition of this form
+ // should return -1 for both crossing targets.
+ virtual int a_crossing_target() const = 0;
+ virtual int b_crossing_target() const = 0;
+
+ // Given a vertex "ab1" that is shared between the two loops, return true if
+ // the two associated wedges (a0, ab1, b2) and (b0, ab1, b2) are equivalent
+ // to an edge crossing. The loop relation is also allowed to maintain its
+ // own internal state, and can return true if it observes any sequence of
+ // wedges that are equivalent to an edge crossing.
+ virtual bool WedgesCross(const S2Point& a0, const S2Point& ab1,
+ const S2Point& a2, const S2Point& b0,
+ const S2Point& b2) = 0;
+};
+
+// RangeIterator is a wrapper over MutableS2ShapeIndex::Iterator with extra
+// methods that are useful for merging the contents of two or more
+// S2ShapeIndexes.
+class RangeIterator {
+ public:
+ // Construct a new RangeIterator positioned at the first cell of the index.
+ explicit RangeIterator(const MutableS2ShapeIndex* index)
+ : it_(index, S2ShapeIndex::BEGIN) {
+ Refresh();
+ }
+
+ // The current S2CellId and cell contents.
+ S2CellId id() const { return it_.id(); }
+ const S2ShapeIndexCell& cell() const { return it_.cell(); }
+
+ // The min and max leaf cell ids covered by the current cell. If Done() is
+ // true, these methods return a value larger than any valid cell id.
+ S2CellId range_min() const { return range_min_; }
+ S2CellId range_max() const { return range_max_; }
+
+ // Various other convenience methods for the current cell.
+ const S2ClippedShape& clipped() const { return cell().clipped(0); }
+
+ int num_edges() const { return clipped().num_edges(); }
+ bool contains_center() const { return clipped().contains_center(); }
+
+ void Next() { it_.Next(); Refresh(); }
+ bool Done() { return it_.done(); }
+
+ // Position the iterator at the first cell that overlaps or follows
+ // "target", i.e. such that range_max() >= target.range_min().
+ void SeekTo(const RangeIterator& target) {
+ it_.Seek(target.range_min());
+ // If the current cell does not overlap "target", it is possible that the
+ // previous cell is the one we are looking for. This can only happen when
+ // the previous cell contains "target" but has a smaller S2CellId.
+ if (it_.done() || it_.id().range_min() > target.range_max()) {
+ if (it_.Prev() && it_.id().range_max() < target.id()) it_.Next();
+ }
+ Refresh();
+ }
+
+ // Position the iterator at the first cell that follows "target", i.e. the
+ // first cell such that range_min() > target.range_max().
+ void SeekBeyond(const RangeIterator& target) {
+ it_.Seek(target.range_max().next());
+ if (!it_.done() && it_.id().range_min() <= target.range_max()) {
+ it_.Next();
+ }
+ Refresh();
+ }
+
+ private:
+ // Updates internal state after the iterator has been repositioned.
+ void Refresh() {
+ range_min_ = id().range_min();
+ range_max_ = id().range_max();
+ }
+
+ MutableS2ShapeIndex::Iterator it_;
+ S2CellId range_min_, range_max_;
+};
+
+// LoopCrosser is a helper class for determining whether two loops cross.
+// It is instantiated twice for each pair of loops to be tested, once for the
+// pair (A,B) and once for the pair (B,A), in order to be able to process
+// edges in either loop nesting order.
+class LoopCrosser {
+ public:
+ // If "swapped" is true, the loops A and B have been swapped. This affects
+ // how arguments are passed to the given loop relation, since for example
+ // A.Contains(B) is not the same as B.Contains(A).
+ LoopCrosser(const S2Loop& a, const S2Loop& b,
+ LoopRelation* relation, bool swapped)
+ : a_(a), b_(b), relation_(relation), swapped_(swapped),
+ a_crossing_target_(relation->a_crossing_target()),
+ b_crossing_target_(relation->b_crossing_target()),
+ b_query_(&b.index_) {
+ using std::swap;
+ if (swapped) swap(a_crossing_target_, b_crossing_target_);
+ }
+
+ // Return the crossing targets for the loop relation, taking into account
+ // whether the loops have been swapped.
+ int a_crossing_target() const { return a_crossing_target_; }
+ int b_crossing_target() const { return b_crossing_target_; }
+
+ // Given two iterators positioned such that ai->id().Contains(bi->id()),
+ // return true if there is a crossing relationship anywhere within ai->id().
+ // Specifically, this method returns true if there is an edge crossing, a
+ // wedge crossing, or a point P that matches both "crossing targets".
+ // Advances both iterators past ai->id().
+ bool HasCrossingRelation(RangeIterator* ai, RangeIterator* bi);
+
+ // Given two index cells, return true if there are any edge crossings or
+ // wedge crossings within those cells.
+ bool CellCrossesCell(const S2ClippedShape& a_clipped,
+ const S2ClippedShape& b_clipped);
+
+ private:
+ // Given two iterators positioned such that ai->id().Contains(bi->id()),
+ // return true if there is an edge crossing or wedge crosssing anywhere
+ // within ai->id(). Advances "bi" (only) past ai->id().
+ bool HasCrossing(RangeIterator* ai, RangeIterator* bi);
+
+ // Given an index cell of A, return true if there are any edge or wedge
+ // crossings with any index cell of B contained within "b_id".
+ bool CellCrossesAnySubcell(const S2ClippedShape& a_clipped, S2CellId b_id);
+
+ // Prepare to check the given edge of loop A for crossings.
+ void StartEdge(int aj);
+
+ // Check the current edge of loop A for crossings with all edges of the
+ // given index cell of loop B.
+ bool EdgeCrossesCell(const S2ClippedShape& b_clipped);
+
+ const S2Loop& a_;
+ const S2Loop& b_;
+ LoopRelation* const relation_;
+ const bool swapped_;
+ int a_crossing_target_, b_crossing_target_;
+
+ // State maintained by StartEdge() and EdgeCrossesCell().
+ S2EdgeCrosser crosser_;
+ int aj_, bj_prev_;
+
+ // Temporary data declared here to avoid repeated memory allocations.
+ S2CrossingEdgeQuery b_query_;
+ vector<const S2ShapeIndexCell*> b_cells_;
+};
+
+inline void LoopCrosser::StartEdge(int aj) {
+ // Start testing the given edge of A for crossings.
+ crosser_.Init(&a_.vertex(aj), &a_.vertex(aj+1));
+ aj_ = aj;
+ bj_prev_ = -2;
+}
+
+inline bool LoopCrosser::EdgeCrossesCell(const S2ClippedShape& b_clipped) {
+ // Test the current edge of A against all edges of "b_clipped".
+ int b_num_edges = b_clipped.num_edges();
+ for (int j = 0; j < b_num_edges; ++j) {
+ int bj = b_clipped.edge(j);
+ if (bj != bj_prev_ + 1) crosser_.RestartAt(&b_.vertex(bj));
+ bj_prev_ = bj;
+ int crossing = crosser_.CrossingSign(&b_.vertex(bj + 1));
+ if (crossing < 0) continue;
+ if (crossing > 0) return true;
+ // We only need to check each shared vertex once, so we only
+ // consider the case where a_vertex(aj_+1) == b_.vertex(bj+1).
+ if (a_.vertex(aj_+1) == b_.vertex(bj+1)) {
+ if (swapped_ ?
+ relation_->WedgesCross(
+ b_.vertex(bj), b_.vertex(bj+1), b_.vertex(bj+2),
+ a_.vertex(aj_), a_.vertex(aj_+2)) :
+ relation_->WedgesCross(
+ a_.vertex(aj_), a_.vertex(aj_+1), a_.vertex(aj_+2),
+ b_.vertex(bj), b_.vertex(bj+2))) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool LoopCrosser::CellCrossesCell(const S2ClippedShape& a_clipped,
+ const S2ClippedShape& b_clipped) {
+ // Test all edges of "a_clipped" against all edges of "b_clipped".
+ int a_num_edges = a_clipped.num_edges();
+ for (int i = 0; i < a_num_edges; ++i) {
+ StartEdge(a_clipped.edge(i));
+ if (EdgeCrossesCell(b_clipped)) return true;
+ }
+ return false;
+}
+
+bool LoopCrosser::CellCrossesAnySubcell(const S2ClippedShape& a_clipped,
+ S2CellId b_id) {
+ // Test all edges of "a_clipped" against all edges of B. The relevant B
+ // edges are guaranteed to be children of "b_id", which lets us find the
+ // correct index cells more efficiently.
+ S2PaddedCell b_root(b_id, 0);
+ int a_num_edges = a_clipped.num_edges();
+ for (int i = 0; i < a_num_edges; ++i) {
+ int aj = a_clipped.edge(i);
+ // Use an S2CrossingEdgeQuery starting at "b_root" to find the index cells
+ // of B that might contain crossing edges.
+ b_query_.GetCells(a_.vertex(aj), a_.vertex(aj+1), b_root, &b_cells_);
+ if (b_cells_.empty()) continue;
+ StartEdge(aj);
+ for (const S2ShapeIndexCell* b_cell : b_cells_) {
+ if (EdgeCrossesCell(b_cell->clipped(0))) return true;
+ }
+ }
+ return false;
+}
+
+bool LoopCrosser::HasCrossing(RangeIterator* ai, RangeIterator* bi) {
+ S2_DCHECK(ai->id().contains(bi->id()));
+ // If ai->id() intersects many edges of B, then it is faster to use
+ // S2CrossingEdgeQuery to narrow down the candidates. But if it intersects
+ // only a few edges, it is faster to check all the crossings directly.
+ // We handle this by advancing "bi" and keeping track of how many edges we
+ // would need to test.
+
+ static const int kEdgeQueryMinEdges = 20; // Tuned using benchmarks.
+ int total_edges = 0;
+ b_cells_.clear();
+ do {
+ if (bi->num_edges() > 0) {
+ total_edges += bi->num_edges();
+ if (total_edges >= kEdgeQueryMinEdges) {
+ // There are too many edges to test them directly, so use
+ // S2CrossingEdgeQuery.
+ if (CellCrossesAnySubcell(ai->clipped(), ai->id())) return true;
+ bi->SeekBeyond(*ai);
+ return false;
+ }
+ b_cells_.push_back(&bi->cell());
+ }
+ bi->Next();
+ } while (bi->id() <= ai->range_max());
+
+ // Test all the edge crossings directly.
+ for (const S2ShapeIndexCell* b_cell : b_cells_) {
+ if (CellCrossesCell(ai->clipped(), b_cell->clipped(0))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool LoopCrosser::HasCrossingRelation(RangeIterator* ai, RangeIterator* bi) {
+ S2_DCHECK(ai->id().contains(bi->id()));
+ if (ai->num_edges() == 0) {
+ if (ai->contains_center() == a_crossing_target_) {
+ // All points within ai->id() satisfy the crossing target for A, so it's
+ // worth iterating through the cells of B to see whether any cell
+ // centers also satisfy the crossing target for B.
+ do {
+ if (bi->contains_center() == b_crossing_target_) return true;
+ bi->Next();
+ } while (bi->id() <= ai->range_max());
+ } else {
+ // The crossing target for A is not satisfied, so we skip over the cells
+ // of B using binary search.
+ bi->SeekBeyond(*ai);
+ }
+ } else {
+ // The current cell of A has at least one edge, so check for crossings.
+ if (HasCrossing(ai, bi)) return true;
+ }
+ ai->Next();
+ return false;
+}
+
+/*static*/ bool S2Loop::HasCrossingRelation(const S2Loop& a, const S2Loop& b,
+ LoopRelation* relation) {
+ // We look for S2CellId ranges where the indexes of A and B overlap, and
+ // then test those edges for crossings.
+ RangeIterator ai(&a.index_), bi(&b.index_);
+ LoopCrosser ab(a, b, relation, false); // Tests edges of A against B
+ LoopCrosser ba(b, a, relation, true); // Tests edges of B against A
+ while (!ai.Done() || !bi.Done()) {
+ if (ai.range_max() < bi.range_min()) {
+ // The A and B cells don't overlap, and A precedes B.
+ ai.SeekTo(bi);
+ } else if (bi.range_max() < ai.range_min()) {
+ // The A and B cells don't overlap, and B precedes A.
+ bi.SeekTo(ai);
+ } else {
+ // One cell contains the other. Determine which cell is larger.
+ int64 ab_relation = ai.id().lsb() - bi.id().lsb();
+ if (ab_relation > 0) {
+ // A's index cell is larger.
+ if (ab.HasCrossingRelation(&ai, &bi)) return true;
+ } else if (ab_relation < 0) {
+ // B's index cell is larger.
+ if (ba.HasCrossingRelation(&bi, &ai)) return true;
+ } else {
+ // The A and B cells are the same. Since the two cells have the same
+ // center point P, check whether P satisfies the crossing targets.
+ if (ai.contains_center() == ab.a_crossing_target() &&
+ bi.contains_center() == ab.b_crossing_target()) {
+ return true;
+ }
+ // Otherwise test all the edge crossings directly.
+ if (ai.num_edges() > 0 && bi.num_edges() > 0 &&
+ ab.CellCrossesCell(ai.clipped(), bi.clipped())) {
+ return true;
+ }
+ ai.Next();
+ bi.Next();
+ }
+ }
+ }
+ return false;
+}
+
+// Loop relation for Contains().
+class ContainsRelation : public LoopRelation {
+ public:
+ ContainsRelation() : found_shared_vertex_(false) {}
+ bool found_shared_vertex() const { return found_shared_vertex_; }
+
+ // If A.Contains(P) == false && B.Contains(P) == true, it is equivalent to
+ // having an edge crossing (i.e., Contains returns false).
+ int a_crossing_target() const override { return false; }
+ int b_crossing_target() const override { return true; }
+
+ bool WedgesCross(const S2Point& a0, const S2Point& ab1, const S2Point& a2,
+ const S2Point& b0, const S2Point& b2) override {
+ found_shared_vertex_ = true;
+ return !S2::WedgeContains(a0, ab1, a2, b0, b2);
+ }
+
+ private:
+ bool found_shared_vertex_;
+};
+
+bool S2Loop::Contains(const S2Loop* b) const {
+ // For this loop A to contains the given loop B, all of the following must
+ // be true:
+ //
+ // (1) There are no edge crossings between A and B except at vertices.
+ //
+ // (2) At every vertex that is shared between A and B, the local edge
+ // ordering implies that A contains B.
+ //
+ // (3) If there are no shared vertices, then A must contain a vertex of B
+ // and B must not contain a vertex of A. (An arbitrary vertex may be
+ // chosen in each case.)
+ //
+ // The second part of (3) is necessary to detect the case of two loops whose
+ // union is the entire sphere, i.e. two loops that contains each other's
+ // boundaries but not each other's interiors.
+ if (!subregion_bound_.Contains(b->bound_)) return false;
+
+ // Special cases to handle either loop being empty or full.
+ if (is_empty_or_full() || b->is_empty_or_full()) {
+ return is_full() || b->is_empty();
+ }
+
+ // Check whether there are any edge crossings, and also check the loop
+ // relationship at any shared vertices.
+ ContainsRelation relation;
+ if (HasCrossingRelation(*this, *b, &relation)) return false;
+
+ // There are no crossings, and if there are any shared vertices then A
+ // contains B locally at each shared vertex.
+ if (relation.found_shared_vertex()) return true;
+
+ // Since there are no edge intersections or shared vertices, we just need to
+ // test condition (3) above. We can skip this test if we discovered that A
+ // contains at least one point of B while checking for edge crossings.
+ if (!Contains(b->vertex(0))) return false;
+
+ // We still need to check whether (A union B) is the entire sphere.
+ // Normally this check is very cheap due to the bounding box precondition.
+ if ((b->subregion_bound_.Contains(bound_) ||
+ b->bound_.Union(bound_).is_full()) && b->Contains(vertex(0))) {
+ return false;
+ }
+ return true;
+}
+
+
+// Loop relation for Intersects().
+class IntersectsRelation : public LoopRelation {
+ public:
+ IntersectsRelation() : found_shared_vertex_(false) {}
+ bool found_shared_vertex() const { return found_shared_vertex_; }
+
+ // If A.Contains(P) == true && B.Contains(P) == true, it is equivalent to
+ // having an edge crossing (i.e., Intersects returns true).
+ int a_crossing_target() const override { return true; }
+ int b_crossing_target() const override { return true; }
+
+ bool WedgesCross(const S2Point& a0, const S2Point& ab1, const S2Point& a2,
+ const S2Point& b0, const S2Point& b2) override {
+ found_shared_vertex_ = true;
+ return S2::WedgeIntersects(a0, ab1, a2, b0, b2);
+ }
+
+ private:
+ bool found_shared_vertex_;
+};
+
+bool S2Loop::Intersects(const S2Loop* b) const {
+ // a->Intersects(b) if and only if !a->Complement()->Contains(b).
+ // This code is similar to Contains(), but is optimized for the case
+ // where both loops enclose less than half of the sphere.
+ if (!bound_.Intersects(b->bound_)) return false;
+
+ // Check whether there are any edge crossings, and also check the loop
+ // relationship at any shared vertices.
+ IntersectsRelation relation;
+ if (HasCrossingRelation(*this, *b, &relation)) return true;
+ if (relation.found_shared_vertex()) return false;
+
+ // Since there are no edge intersections or shared vertices, the loops
+ // intersect only if A contains B, B contains A, or the two loops contain
+ // each other's boundaries. These checks are usually cheap because of the
+ // bounding box preconditions. Note that neither loop is empty (because of
+ // the bounding box check above), so it is safe to access vertex(0).
+
+ // Check whether A contains B, or A and B contain each other's boundaries.
+ // (Note that A contains all the vertices of B in either case.)
+ if (subregion_bound_.Contains(b->bound_) ||
+ bound_.Union(b->bound_).is_full()) {
+ if (Contains(b->vertex(0))) return true;
+ }
+ // Check whether B contains A.
+ if (b->subregion_bound_.Contains(bound_)) {
+ if (b->Contains(vertex(0))) return true;
+ }
+ return false;
+}
+
+// Returns true if the wedge (a0, ab1, a2) contains the "semiwedge" defined as
+// any non-empty open set of rays immediately CCW from the edge (ab1, b2). If
+// "reverse_b" is true, then substitute "clockwise" for "CCW"; this simulates
+// what would happen if the direction of loop B was reversed.
+inline static bool WedgeContainsSemiwedge(const S2Point& a0, const S2Point& ab1,
+ const S2Point& a2, const S2Point& b2,
+ bool reverse_b) {
+ if (b2 == a0 || b2 == a2) {
+ // We have a shared or reversed edge.
+ return (b2 == a0) == reverse_b;
+ } else {
+ return s2pred::OrderedCCW(a0, a2, b2, ab1);
+ }
+}
+
+// Loop relation for CompareBoundary().
+class CompareBoundaryRelation : public LoopRelation {
+ public:
+ explicit CompareBoundaryRelation(bool reverse_b):
+ reverse_b_(reverse_b), found_shared_vertex_(false),
+ contains_edge_(false), excludes_edge_(false) {
+ }
+ bool found_shared_vertex() const { return found_shared_vertex_; }
+ bool contains_edge() const { return contains_edge_; }
+
+ // The CompareBoundary relation does not have a useful early-exit condition,
+ // so we return -1 for both crossing targets.
+ //
+ // Aside: A possible early exit condition could be based on the following.
+ // If A contains a point of both B and ~B, then A intersects Boundary(B).
+ // If ~A contains a point of both B and ~B, then ~A intersects Boundary(B).
+ // So if the intersections of {A, ~A} with {B, ~B} are all non-empty,
+ // the return value is 0, i.e., Boundary(A) intersects Boundary(B).
+ // Unfortunately it isn't worth detecting this situation because by the
+ // time we have seen a point in all four intersection regions, we are also
+ // guaranteed to have seen at least one pair of crossing edges.
+ int a_crossing_target() const override { return -1; }
+ int b_crossing_target() const override { return -1; }
+
+ bool WedgesCross(const S2Point& a0, const S2Point& ab1, const S2Point& a2,
+ const S2Point& b0, const S2Point& b2) override {
+ // Because we don't care about the interior of B, only its boundary, it is
+ // sufficient to check whether A contains the semiwedge (ab1, b2).
+ found_shared_vertex_ = true;
+ if (WedgeContainsSemiwedge(a0, ab1, a2, b2, reverse_b_)) {
+ contains_edge_ = true;
+ } else {
+ excludes_edge_ = true;
+ }
+ return contains_edge_ & excludes_edge_;
+ }
+
+ protected:
+ const bool reverse_b_; // True if loop B should be reversed.
+ bool found_shared_vertex_; // True if any wedge was processed.
+ bool contains_edge_; // True if any edge of B is contained by A.
+ bool excludes_edge_; // True if any edge of B is excluded by A.
+};
+
+int S2Loop::CompareBoundary(const S2Loop* b) const {
+ S2_DCHECK(!is_empty() && !b->is_empty());
+ S2_DCHECK(!b->is_full() || !b->is_hole());
+
+ // The bounds must intersect for containment or crossing.
+ if (!bound_.Intersects(b->bound_)) return -1;
+
+ // Full loops are handled as though the loop surrounded the entire sphere.
+ if (is_full()) return 1;
+ if (b->is_full()) return -1;
+
+ // Check whether there are any edge crossings, and also check the loop
+ // relationship at any shared vertices.
+ CompareBoundaryRelation relation(b->is_hole());
+ if (HasCrossingRelation(*this, *b, &relation)) return 0;
+ if (relation.found_shared_vertex()) {
+ return relation.contains_edge() ? 1 : -1;
+ }
+
+ // There are no edge intersections or shared vertices, so we can check
+ // whether A contains an arbitrary vertex of B.
+ return Contains(b->vertex(0)) ? 1 : -1;
+}
+
+bool S2Loop::ContainsNonCrossingBoundary(const S2Loop* b, bool reverse_b)
+ const {
+ S2_DCHECK(!is_empty() && !b->is_empty());
+ S2_DCHECK(!b->is_full() || !reverse_b);
+
+ // The bounds must intersect for containment.
+ if (!bound_.Intersects(b->bound_)) return false;
+
+ // Full loops are handled as though the loop surrounded the entire sphere.
+ if (is_full()) return true;
+ if (b->is_full()) return false;
+
+ int m = FindVertex(b->vertex(0));
+ if (m < 0) {
+ // Since vertex b0 is not shared, we can check whether A contains it.
+ return Contains(b->vertex(0));
+ }
+ // Otherwise check whether the edge (b0, b1) is contained by A.
+ return WedgeContainsSemiwedge(vertex(m-1), vertex(m), vertex(m+1),
+ b->vertex(1), reverse_b);
+}
+
+bool S2Loop::ContainsNested(const S2Loop* b) const {
+ if (!subregion_bound_.Contains(b->bound_)) return false;
+
+ // Special cases to handle either loop being empty or full. Also bail out
+ // when B has no vertices to avoid heap overflow on the vertex(1) call
+ // below. (This method is called during polygon initialization before the
+ // client has an opportunity to call IsValid().)
+ if (is_empty_or_full() || b->num_vertices() < 2) {
+ return is_full() || b->is_empty();
+ }
+
+ // We are given that A and B do not share any edges, and that either one
+ // loop contains the other or they do not intersect.
+ int m = FindVertex(b->vertex(1));
+ if (m < 0) {
+ // Since b->vertex(1) is not shared, we can check whether A contains it.
+ return Contains(b->vertex(1));
+ }
+ // Check whether the edge order around b->vertex(1) is compatible with
+ // A containing B.
+ return S2::WedgeContains(vertex(m-1), vertex(m), vertex(m+1),
+ b->vertex(0), b->vertex(2));
+}
+
+bool S2Loop::Equals(const S2Loop* b) const {
+ if (num_vertices() != b->num_vertices()) return false;
+ for (int i = 0; i < num_vertices(); ++i) {
+ if (vertex(i) != b->vertex(i)) return false;
+ }
+ return true;
+}
+
+bool S2Loop::BoundaryEquals(const S2Loop* b) const {
+ if (num_vertices() != b->num_vertices()) return false;
+
+ // Special case to handle empty or full loops. Since they have the same
+ // number of vertices, if one loop is empty/full then so is the other.
+ if (is_empty_or_full()) return is_empty() == b->is_empty();
+
+ for (int offset = 0; offset < num_vertices(); ++offset) {
+ if (vertex(offset) == b->vertex(0)) {
+ // There is at most one starting offset since loop vertices are unique.
+ for (int i = 0; i < num_vertices(); ++i) {
+ if (vertex(i + offset) != b->vertex(i)) return false;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool S2Loop::BoundaryApproxEquals(const S2Loop& b, S1Angle max_error) const {
+ if (num_vertices() != b.num_vertices()) return false;
+
+ // Special case to handle empty or full loops. Since they have the same
+ // number of vertices, if one loop is empty/full then so is the other.
+ if (is_empty_or_full()) return is_empty() == b.is_empty();
+
+ for (int offset = 0; offset < num_vertices(); ++offset) {
+ if (S2::ApproxEquals(vertex(offset), b.vertex(0), max_error)) {
+ bool success = true;
+ for (int i = 0; i < num_vertices(); ++i) {
+ if (!S2::ApproxEquals(vertex(i + offset), b.vertex(i), max_error)) {
+ success = false;
+ break;
+ }
+ }
+ if (success) return true;
+ // Otherwise continue looping. There may be more than one candidate
+ // starting offset since vertices are only matched approximately.
+ }
+ }
+ return false;
+}
+
+static bool MatchBoundaries(const S2Loop& a, const S2Loop& b, int a_offset,
+ S1Angle max_error) {
+ // The state consists of a pair (i,j). A state transition consists of
+ // incrementing either "i" or "j". "i" can be incremented only if
+ // a(i+1+a_offset) is near the edge from b(j) to b(j+1), and a similar rule
+ // applies to "j". The function returns true iff we can proceed all the way
+ // around both loops in this way.
+ //
+ // Note that when "i" and "j" can both be incremented, sometimes only one
+ // choice leads to a solution. We handle this using a stack and
+ // backtracking. We also keep track of which states have already been
+ // explored to avoid duplicating work.
+
+ vector<pair<int, int>> pending;
+ set<pair<int, int>> done;
+ pending.push_back(std::make_pair(0, 0));
+ while (!pending.empty()) {
+ int i = pending.back().first;
+ int j = pending.back().second;
+ pending.pop_back();
+ if (i == a.num_vertices() && j == b.num_vertices()) {
+ return true;
+ }
+ done.insert(std::make_pair(i, j));
+
+ // If (i == na && offset == na-1) where na == a->num_vertices(), then
+ // then (i+1+offset) overflows the [0, 2*na-1] range allowed by vertex().
+ // So we reduce the range if necessary.
+ int io = i + a_offset;
+ if (io >= a.num_vertices()) io -= a.num_vertices();
+
+ if (i < a.num_vertices() && done.count(std::make_pair(i + 1, j)) == 0 &&
+ S2::GetDistance(a.vertex(io + 1), b.vertex(j),
+ b.vertex(j + 1)) <= max_error) {
+ pending.push_back(std::make_pair(i + 1, j));
+ }
+ if (j < b.num_vertices() && done.count(std::make_pair(i, j + 1)) == 0 &&
+ S2::GetDistance(b.vertex(j + 1), a.vertex(io),
+ a.vertex(io + 1)) <= max_error) {
+ pending.push_back(std::make_pair(i, j + 1));
+ }
+ }
+ return false;
+}
+
+bool S2Loop::BoundaryNear(const S2Loop& b, S1Angle max_error) const {
+ // Special case to handle empty or full loops.
+ if (is_empty_or_full() || b.is_empty_or_full()) {
+ return (is_empty() && b.is_empty()) || (is_full() && b.is_full());
+ }
+
+ for (int a_offset = 0; a_offset < num_vertices(); ++a_offset) {
+ if (MatchBoundaries(*this, b, a_offset, max_error)) return true;
+ }
+ return false;
+}
+
+void S2Loop::GetXYZFaceSiTiVertices(S2XYZFaceSiTi* vertices) const {
+ for (int i = 0; i < num_vertices(); ++i) {
+ vertices[i].xyz = vertex(i);
+ vertices[i].cell_level = S2::XYZtoFaceSiTi(vertices[i].xyz,
+ &vertices[i].face, &vertices[i].si, &vertices[i].ti);
+ }
+}
+
+void S2Loop::EncodeCompressed(Encoder* encoder, const S2XYZFaceSiTi* vertices,
+ int snap_level) const {
+ // Ensure enough for the data we write before S2EncodePointsCompressed.
+ // S2EncodePointsCompressed ensures its space.
+ encoder->Ensure(Encoder::kVarintMax32);
+ encoder->put_varint32(num_vertices_);
+
+ S2EncodePointsCompressed(MakeSpan(vertices, num_vertices_),
+ snap_level, encoder);
+
+ std::bitset<kNumProperties> properties = GetCompressedEncodingProperties();
+
+ // Ensure enough only for what we write. Let the bound ensure its own
+ // space.
+ encoder->Ensure(2 * Encoder::kVarintMax32);
+ encoder->put_varint32(properties.to_ulong());
+ encoder->put_varint32(depth_);
+ if (properties.test(kBoundEncoded)) {
+ bound_.Encode(encoder);
+ }
+ S2_DCHECK_GE(encoder->avail(), 0);
+}
+
+bool S2Loop::DecodeCompressed(Decoder* decoder, int snap_level) {
+ // get_varint32 takes a uint32*, but num_vertices_ is signed.
+ // Decode to a temporary variable to avoid reinterpret_cast.
+ uint32 unsigned_num_vertices;
+ if (!decoder->get_varint32(&unsigned_num_vertices)) {
+ return false;
+ }
+ if (unsigned_num_vertices == 0 ||
+ unsigned_num_vertices > FLAGS_s2polygon_decode_max_num_vertices) {
+ return false;
+ }
+ ClearIndex();
+ if (owns_vertices_) delete[] vertices_;
+ num_vertices_ = unsigned_num_vertices;
+ vertices_ = new S2Point[num_vertices_];
+ owns_vertices_ = true;
+
+ if (!S2DecodePointsCompressed(decoder, snap_level,
+ MakeSpan(vertices_, num_vertices_))) {
+ return false;
+ }
+ uint32 properties_uint32;
+ if (!decoder->get_varint32(&properties_uint32)) {
+ return false;
+ }
+ const std::bitset<kNumProperties> properties(properties_uint32);
+ origin_inside_ = properties.test(kOriginInside);
+
+ uint32 unsigned_depth;
+ if (!decoder->get_varint32(&unsigned_depth)) {
+ return false;
+ }
+ depth_ = unsigned_depth;
+
+ if (properties.test(kBoundEncoded)) {
+ if (!bound_.Decode(decoder)) {
+ return false;
+ }
+ subregion_bound_ = S2LatLngRectBounder::ExpandForSubregions(bound_);
+ } else {
+ InitBound();
+ }
+ InitIndex();
+ return true;
+}
+
+std::bitset<kNumProperties> S2Loop::GetCompressedEncodingProperties() const {
+ std::bitset<kNumProperties> properties;
+ if (origin_inside_) {
+ properties.set(kOriginInside);
+ }
+
+ // Write whether there is a bound so we can change the threshold later.
+ // Recomputing the bound multiplies the decode time taken per vertex
+ // by a factor of about 3.5. Without recomputing the bound, decode
+ // takes approximately 125 ns / vertex. A loop with 63 vertices
+ // encoded without the bound will take ~30us to decode, which is
+ // acceptable. At ~3.5 bytes / vertex without the bound, adding
+ // the bound will increase the size by <15%, which is also acceptable.
+ static const int kMinVerticesForBound = 64;
+ if (num_vertices_ >= kMinVerticesForBound) {
+ properties.set(kBoundEncoded);
+ }
+ return properties;
+}
+
+/* static */
+std::unique_ptr<S2Loop> S2Loop::MakeRegularLoop(const S2Point& center,
+ S1Angle radius,
+ int num_vertices) {
+ Matrix3x3_d m;
+ S2::GetFrame(center, &m); // TODO(ericv): Return by value
+ return MakeRegularLoop(m, radius, num_vertices);
+}
+
+/* static */
+std::unique_ptr<S2Loop> S2Loop::MakeRegularLoop(const Matrix3x3_d& frame,
+ S1Angle radius,
+ int num_vertices) {
+ // We construct the loop in the given frame coordinates, with the center at
+ // (0, 0, 1). For a loop of radius "r", the loop vertices have the form
+ // (x, y, z) where x^2 + y^2 = sin(r) and z = cos(r). The distance on the
+ // sphere (arc length) from each vertex to the center is acos(cos(r)) = r.
+ double z = cos(radius.radians());
+ double r = sin(radius.radians());
+ double radian_step = 2 * M_PI / num_vertices;
+ vector<S2Point> vertices;
+ for (int i = 0; i < num_vertices; ++i) {
+ double angle = i * radian_step;
+ S2Point p(r * cos(angle), r * sin(angle), z);
+ vertices.push_back(S2::FromFrame(frame, p).Normalize());
+ }
+ return make_unique<S2Loop>(vertices);
+}
+
+size_t S2Loop::SpaceUsed() const {
+ size_t size = sizeof(*this);
+ size += num_vertices() * sizeof(S2Point);
+ // index_ itself is already included in sizeof(*this).
+ size += index_.SpaceUsed() - sizeof(index_);
+ return size;
+}
+
+int S2Loop::Shape::num_chains() const {
+ return loop_->is_empty() ? 0 : 1;
+}
+
+S2Shape::Chain S2Loop::Shape::chain(int i) const {
+ S2_DCHECK_EQ(i, 0);
+ return Chain(0, Shape::num_edges()); // Avoid virtual call.
+}
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2loop_measures.h"
+
+#include <cfloat>
+#include <cmath>
+#include <vector>
+#include "s2/base/logging.h"
+#include "s2/third_party/absl/container/inlined_vector.h"
+#include "s2/s1angle.h"
+#include "s2/s2centroids.h"
+#include "s2/s2edge_distances.h"
+#include "s2/s2measures.h"
+#include "s2/s2pointutil.h"
+
+using std::fabs;
+using std::max;
+using std::min;
+using std::vector;
+
+namespace S2 {
+
+S1Angle GetPerimeter(S2PointLoopSpan loop) {
+ S1Angle perimeter = S1Angle::Zero();
+ if (loop.size() <= 1) return perimeter;
+ for (int i = 0; i < loop.size(); ++i) {
+ perimeter += S1Angle(loop[i], loop[i + 1]);
+ }
+ return perimeter;
+}
+
+double GetArea(S2PointLoopSpan loop) {
+ double area = GetSignedArea(loop);
+ S2_DCHECK_LE(fabs(area), 2 * M_PI);
+ if (area < 0.0) area += 4 * M_PI;
+ return area;
+}
+
+double GetSignedArea(S2PointLoopSpan loop) {
+ // It is suprisingly difficult to compute the area of a loop robustly. The
+ // main issues are (1) whether degenerate loops are considered to be CCW or
+ // not (i.e., whether their area is close to 0 or 4*Pi), and (2) computing
+ // the areas of small loops with good relative accuracy.
+ //
+ // With respect to degeneracies, we would like GetArea() to be consistent
+ // with S2Loop::Contains(S2Point) in that loops that contain many points
+ // should have large areas, and loops that contain few points should have
+ // small areas. For example, if a degenerate triangle is considered CCW
+ // according to s2pred::Sign(), then it will contain very few points and
+ // its area should be approximately zero. On the other hand if it is
+ // considered clockwise, then it will contain virtually all points and so
+ // its area should be approximately 4*Pi.
+ //
+ // More precisely, let U be the set of S2Points for which S2::IsUnitLength()
+ // is true, let P(U) be the projection of those points onto the mathematical
+ // unit sphere, and let V(P(U)) be the Voronoi diagram of the projected
+ // points. Then for every loop x, we would like GetArea() to approximately
+ // equal the sum of the areas of the Voronoi regions of the points p for
+ // which x.Contains(p) is true.
+ //
+ // The second issue is that we want to compute the area of small loops
+ // accurately. This requires having good relative precision rather than
+ // good absolute precision. For example, if the area of a loop is 1e-12 and
+ // the error is 1e-15, then the area only has 3 digits of accuracy. (For
+ // reference, 1e-12 is about 40 square meters on the surface of the earth.)
+ // We would like to have good relative accuracy even for small loops.
+ //
+ // To achieve these goals, we combine two different methods of computing the
+ // area. This first method is based on the Gauss-Bonnet theorem, which says
+ // that the area enclosed by the loop equals 2*Pi minus the total geodesic
+ // curvature of the loop (i.e., the sum of the "turning angles" at all the
+ // loop vertices). The big advantage of this method is that as long as we
+ // use s2pred::Sign() to compute the turning angle at each vertex, then
+ // degeneracies are always handled correctly. In other words, if a
+ // degenerate loop is CCW according to the symbolic perturbations used by
+ // s2pred::Sign(), then its turning angle will be approximately 2*Pi.
+ //
+ // The disadvantage of the Gauss-Bonnet method is that its absolute error is
+ // about 2e-15 times the number of vertices (see GetCurvatureMaxError).
+ // So, it cannot compute the area of small loops accurately.
+ //
+ // The second method is based on splitting the loop into triangles and
+ // summing the area of each triangle. To avoid the difficulty and expense
+ // of decomposing the loop into a union of non-overlapping triangles,
+ // instead we compute a signed sum over triangles that may overlap (see the
+ // comments for S2Loop::GetSurfaceIntegral). The advantage of this method
+ // is that the area of each triangle can be computed with much better
+ // relative accuracy (using l'Huilier's theorem). The disadvantage is that
+ // the result is a signed area: CCW loops may yield a small positive value,
+ // while CW loops may yield a small negative value (which is converted to a
+ // positive area by adding 4*Pi). This means that small errors in computing
+ // the signed area may translate into a very large error in the result (if
+ // the sign of the sum is incorrect).
+ //
+ // So, our strategy is to combine these two methods as follows. First we
+ // compute the area using the "signed sum over triangles" approach (since it
+ // is generally more accurate). We also estimate the maximum error in this
+ // result. If the signed area is too close to zero (i.e., zero is within
+ // the error bounds), then we double-check the sign of the result using the
+ // Gauss-Bonnet method. If the two methods disagree, we return the smallest
+ // possible positive or negative area based on the result of GetCurvature().
+ // Otherwise we return the area that we computed originally.
+
+ // The signed area should be between approximately -4*Pi and 4*Pi.
+ // Normalize it to be in the range [-2*Pi, 2*Pi].
+ double area = GetSurfaceIntegral(loop, S2::SignedArea);
+ double max_error = GetCurvatureMaxError(loop);
+ S2_DCHECK_LE(fabs(area), 4 * M_PI + max_error);
+ area = remainder(area, 4 * M_PI);
+
+ // If the area is a small negative or positive number, verify that the sign
+ // of the result is consistent with the loop orientation.
+ if (fabs(area) <= max_error) {
+ double curvature = GetCurvature(loop);
+ // Zero-area loops should have a curvature of approximately +/- 2*Pi.
+ S2_DCHECK(!(area == 0 && curvature == 0));
+ if (curvature == 2 * M_PI) return 0.0; // Degenerate
+ if (area <= 0 && curvature > 0) {
+ return std::numeric_limits<double>::min();
+ }
+ // Full loops are handled by the case below.
+ if (area >= 0 && curvature < 0) {
+ return -std::numeric_limits<double>::min();
+ }
+ }
+ return area;
+}
+
+double GetApproxArea(S2PointLoopSpan loop) {
+ return 2 * M_PI - GetCurvature(loop);
+}
+
+S2PointLoopSpan PruneDegeneracies(S2PointLoopSpan loop,
+ vector<S2Point>* new_vertices) {
+ vector<S2Point>& vertices = *new_vertices;
+ vertices.clear();
+ vertices.reserve(loop.size());
+ for (const S2Point& v : loop) {
+ // Remove duplicate vertices.
+ if (vertices.empty() || v != vertices.back()) {
+ // Remove edge pairs of the form ABA.
+ if (vertices.size() >= 2 && v == vertices.end()[-2]) {
+ vertices.pop_back();
+ } else {
+ vertices.push_back(v);
+ }
+ }
+ }
+ // Check whether the loop was completely degenerate.
+ if (vertices.size() < 3) return S2PointLoopSpan();
+
+ // Otherwise some portion of the loop is guaranteed to be non-degenerate.
+ // However there may still be some degenerate portions to remove.
+ if (vertices[0] == vertices.back()) vertices.pop_back();
+
+ // If the loop begins with BA and ends with A, then there is an edge pair of
+ // the form ABA at the end/start of the loop. Remove all such pairs. As
+ // noted above, this is guaranteed to leave a non-degenerate loop.
+ int k = 0;
+ while (vertices[k + 1] == vertices.end()[-(k + 1)]) ++k;
+ return S2PointLoopSpan(vertices.data() + k, vertices.size() - 2 * k);
+}
+
+double GetCurvature(S2PointLoopSpan loop) {
+ // By convention, a loop with no vertices contains all points on the sphere.
+ if (loop.empty()) return -2 * M_PI;
+
+ // Remove any degeneracies from the loop.
+ vector<S2Point> vertices;
+ loop = PruneDegeneracies(loop, &vertices);
+
+ // If the entire loop was degenerate, it's turning angle is defined as 2*Pi.
+ if (loop.empty()) return 2 * M_PI;
+
+ // To ensure that we get the same result when the vertex order is rotated,
+ // and that the result is negated when the vertex order is reversed, we need
+ // to add up the individual turn angles in a consistent order. (In general,
+ // adding up a set of numbers in a different order can change the sum due to
+ // rounding errors.)
+ //
+ // Furthermore, if we just accumulate an ordinary sum then the worst-case
+ // error is quadratic in the number of vertices. (This can happen with
+ // spiral shapes, where the partial sum of the turning angles can be linear
+ // in the number of vertices.) To avoid this we use the Kahan summation
+ // algorithm (http://en.wikipedia.org/wiki/Kahan_summation_algorithm).
+ LoopOrder order = GetCanonicalLoopOrder(loop);
+ int i = order.first, dir = order.dir, n = loop.size();
+ double sum = S2::TurnAngle(loop[(i + n - dir) % n], loop[i],
+ loop[(i + dir) % n]);
+ double compensation = 0; // Kahan summation algorithm
+ while (--n > 0) {
+ i += dir;
+ double angle = S2::TurnAngle(loop[i - dir], loop[i], loop[i + dir]);
+ double old_sum = sum;
+ angle += compensation;
+ sum += angle;
+ compensation = (old_sum - sum) + angle;
+ }
+ constexpr double kMaxCurvature = 2 * M_PI - 4 * DBL_EPSILON;
+ sum += compensation;
+ return max(-kMaxCurvature, min(kMaxCurvature, dir * sum));
+}
+
+double GetCurvatureMaxError(S2PointLoopSpan loop) {
+ // The maximum error can be bounded as follows:
+ // 2.24 * DBL_EPSILON for RobustCrossProd(b, a)
+ // 2.24 * DBL_EPSILON for RobustCrossProd(c, b)
+ // 3.25 * DBL_EPSILON for Angle()
+ // 2.00 * DBL_EPSILON for each addition in the Kahan summation
+ // ------------------
+ // 9.73 * DBL_EPSILON
+ //
+ // TODO(ericv): This error estimate is approximate. There are two issues:
+ // (1) SignedArea needs some improvements to ensure that its error is
+ // actually never higher than GirardArea, and (2) although the number of
+ // triangles in the sum is typically N-2, in theory it could be as high as
+ // 2*N for pathological inputs. But in other respects this error bound is
+ // very conservative since it assumes that the maximum error is achieved on
+ // every triangle.
+ const double kMaxErrorPerVertex = 9.73 * DBL_EPSILON;
+ return kMaxErrorPerVertex * loop.size();
+}
+
+S2Point GetCentroid(S2PointLoopSpan loop) {
+ // GetSurfaceIntegral() returns either the integral of position over loop
+ // interior, or the negative of the integral of position over the loop
+ // exterior. But these two values are the same (!), because the integral of
+ // position over the entire sphere is (0, 0, 0).
+ return GetSurfaceIntegral(loop, S2::TrueCentroid);
+}
+
+static inline bool IsOrderLess(LoopOrder order1, LoopOrder order2,
+ S2PointLoopSpan loop) {
+ if (order1 == order2) return false;
+
+ int i1 = order1.first, i2 = order2.first;
+ int dir1 = order1.dir, dir2 = order2.dir;
+ S2_DCHECK_EQ(loop[i1], loop[i2]);
+ for (int n = loop.size(); --n > 0; ) {
+ i1 += dir1;
+ i2 += dir2;
+ if (loop[i1] < loop[i2]) return true;
+ if (loop[i1] > loop[i2]) return false;
+ }
+ return false;
+}
+
+LoopOrder GetCanonicalLoopOrder(S2PointLoopSpan loop) {
+ // In order to handle loops with duplicate vertices and/or degeneracies, we
+ // return the LoopOrder that minimizes the entire corresponding vertex
+ // *sequence*. For example, suppose that vertices are sorted
+ // alphabetically, and consider the loop CADBAB. The canonical loop order
+ // would be (4, 1), corresponding to the vertex sequence ABCADB. (For
+ // comparison, loop order (4, -1) yields the sequence ABDACB.)
+ //
+ // If two or more loop orders yield identical minimal vertex sequences, then
+ // it doesn't matter which one we return (since they yield the same result).
+
+ // For efficiency, we divide the process into two steps. First we find the
+ // smallest vertex, and the set of vertex indices where that vertex occurs
+ // (noting that the loop may contain duplicate vertices). Then we consider
+ // both possible directions starting from each such vertex index, and return
+ // the LoopOrder corresponding to the smallest vertex sequence.
+ int n = loop.size();
+ if (n == 0) return LoopOrder(0, 1);
+
+ absl::InlinedVector<int, 4> min_indices;
+ min_indices.push_back(0);
+ for (int i = 1; i < n; ++i) {
+ if (loop[i] <= loop[min_indices[0]]) {
+ if (loop[i] < loop[min_indices[0]]) min_indices.clear();
+ min_indices.push_back(i);
+ }
+ }
+ LoopOrder min_order(min_indices[0], 1);
+ for (int min_index : min_indices) {
+ LoopOrder order1(min_index, 1);
+ LoopOrder order2(min_index + n, -1);
+ if (IsOrderLess(order1, min_order, loop)) min_order = order1;
+ if (IsOrderLess(order2, min_order, loop)) min_order = order2;
+ }
+ return min_order;
+}
+
+bool IsNormalized(S2PointLoopSpan loop) {
+ // We allow some error so that hemispheres are always considered normalized.
+ //
+ // TODO(ericv): This is no longer required by the S2Polygon implementation,
+ // so alternatively we could create the invariant that a loop is normalized
+ // if and only if its complement is not normalized.
+ return GetCurvature(loop) >= -GetCurvatureMaxError(loop);
+}
+
+std::ostream& operator<<(std::ostream& os, LoopOrder order) {
+ return os << "(" << order.first << ", " << order.dir << ")";
+}
+
+} // namespace S2
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "s2/s2max_distance_targets.h"
+
+#include <memory>
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/s1angle.h"
+#include "s2/s2cap.h"
+#include "s2/s2cell.h"
+#include "s2/s2edge_distances.h"
+#include "s2/s2furthest_edge_query.h"
+#include "s2/s2shape_index_region.h"
+#include "s2/s2text_format.h"
+
+////////////////// Point Target ////////////////////
+
+// This method returns an S2Cap that bounds the antipode of the target. (This
+// is the set of points whose S2MaxDistance to the target is
+// S2MaxDistance::Zero().)
+S2Cap S2MaxDistancePointTarget::GetCapBound() {
+ return S2Cap(-point_, S1ChordAngle::Zero());
+}
+
+bool S2MaxDistancePointTarget::UpdateMinDistance(
+ const S2Point& p, S2MaxDistance* min_dist) {
+ return min_dist->UpdateMin(S2MaxDistance(S1ChordAngle(p, point_)));
+}
+
+bool S2MaxDistancePointTarget::UpdateMinDistance(
+ const S2Point& v0, const S2Point& v1, S2MaxDistance* min_dist) {
+ S1ChordAngle dist(*min_dist);
+ if (S2::UpdateMaxDistance(point_, v0, v1, &dist)) {
+ min_dist->UpdateMin(S2MaxDistance(dist));
+ return true;
+ }
+ return false;
+}
+
+bool S2MaxDistancePointTarget::UpdateMinDistance(
+ const S2Cell& cell, S2MaxDistance* min_dist) {
+ return min_dist->UpdateMin(S2MaxDistance(cell.GetMaxDistance(point_)));
+}
+
+bool S2MaxDistancePointTarget::VisitContainingShapes(
+ const S2ShapeIndex& index, const ShapeVisitor& visitor) {
+ // For furthest points, we visit the polygons whose interior contains the
+ // antipode of the target point. (These are the polygons whose
+ // S2MaxDistance to the target is S2MaxDistance::Zero().)
+ return MakeS2ContainsPointQuery(&index).VisitContainingShapes(
+ -point_, [this, &visitor](S2Shape* shape) {
+ return visitor(shape, point_);
+ });
+}
+
+////////////////// Edge Target ////////////////////
+
+// This method returns an S2Cap that bounds the antipode of the target. (This
+// is the set of points whose S2MaxDistance to the target is
+// S2MaxDistance::Zero().)
+S2Cap S2MaxDistanceEdgeTarget::GetCapBound() {
+ // The following computes a radius equal to half the edge length in an
+ // efficient and numerically stable way.
+ double d2 = S1ChordAngle(a_, b_).length2();
+ double r2 = (0.5 * d2) / (1 + sqrt(1 - 0.25 * d2));
+ return S2Cap(-(a_ + b_).Normalize(), S1ChordAngle::FromLength2(r2));
+}
+
+bool S2MaxDistanceEdgeTarget::UpdateMinDistance(
+ const S2Point& p, S2MaxDistance* min_dist) {
+ S1ChordAngle dist(*min_dist);
+ if (S2::UpdateMaxDistance(p, a_, b_, &dist)) {
+ min_dist->UpdateMin(S2MaxDistance(dist));
+ return true;
+ }
+ return false;
+}
+
+bool S2MaxDistanceEdgeTarget::UpdateMinDistance(
+ const S2Point& v0, const S2Point& v1, S2MaxDistance* min_dist) {
+ S1ChordAngle dist(*min_dist);
+ if (S2::UpdateEdgePairMaxDistance(a_, b_, v0, v1, &dist)) {
+ min_dist->UpdateMin(S2MaxDistance(dist));
+ return true;
+ }
+ return false;
+}
+
+bool S2MaxDistanceEdgeTarget::UpdateMinDistance(
+ const S2Cell& cell, S2MaxDistance* min_dist) {
+ return min_dist->UpdateMin(S2MaxDistance(cell.GetMaxDistance(a_, b_)));
+}
+
+bool S2MaxDistanceEdgeTarget::VisitContainingShapes(
+ const S2ShapeIndex& index, const ShapeVisitor& visitor) {
+ // We only need to test one edge point. That is because the method *must*
+ // visit a polygon if it fully contains the target, and *is allowed* to
+ // visit a polygon if it intersects the target. If the tested vertex is not
+ // contained, we know the full edge is not contained; if the tested vertex is
+ // contained, then the edge either is fully contained (must be visited) or it
+ // intersects (is allowed to be visited). We visit the center of the edge so
+ // that edge AB gives identical results to BA.
+ S2MaxDistancePointTarget target((a_ + b_).Normalize());
+ return target.VisitContainingShapes(index, visitor);
+}
+
+////////////////// Cell Target ////////////////////
+
+// This method returns an S2Cap that bounds the antipode of the target. (This
+// is the set of points whose S2MaxDistance to the target is
+// S2MaxDistance::Zero().)
+S2Cap S2MaxDistanceCellTarget::GetCapBound() {
+ S2Cap cap = cell_.GetCapBound();
+ return S2Cap(-cap.center(), cap.radius());
+}
+
+bool S2MaxDistanceCellTarget::UpdateMinDistance(
+ const S2Point& p, S2MaxDistance* min_dist) {
+ return min_dist->UpdateMin(S2MaxDistance(cell_.GetMaxDistance(p)));
+}
+
+bool S2MaxDistanceCellTarget::UpdateMinDistance(
+ const S2Point& v0, const S2Point& v1, S2MaxDistance* min_dist) {
+ return min_dist->UpdateMin(S2MaxDistance(cell_.GetMaxDistance(v0, v1)));
+}
+
+bool S2MaxDistanceCellTarget::UpdateMinDistance(
+ const S2Cell& cell, S2MaxDistance* min_dist) {
+ return min_dist->UpdateMin(S2MaxDistance(cell_.GetMaxDistance(cell)));
+}
+
+bool S2MaxDistanceCellTarget::VisitContainingShapes(
+ const S2ShapeIndex& index, const ShapeVisitor& visitor) {
+ // We only need to check one point here - cell center is simplest.
+ // See comment at S2MaxDistanceEdgeTarget::VisitContainingShapes.
+ S2MaxDistancePointTarget target(cell_.GetCenter());
+ return target.VisitContainingShapes(index, visitor);
+}
+
+////////////////// Index Target ////////////////////
+
+S2MaxDistanceShapeIndexTarget::S2MaxDistanceShapeIndexTarget(
+ const S2ShapeIndex* index)
+ : index_(index), query_(absl::make_unique<S2FurthestEdgeQuery>(index)) {
+}
+
+S2MaxDistanceShapeIndexTarget::~S2MaxDistanceShapeIndexTarget() {
+}
+
+bool S2MaxDistanceShapeIndexTarget::include_interiors() const {
+ return query_->options().include_interiors();
+}
+
+void S2MaxDistanceShapeIndexTarget::set_include_interiors(
+ bool include_interiors) {
+ query_->mutable_options()->set_include_interiors(include_interiors);
+}
+
+bool S2MaxDistanceShapeIndexTarget::use_brute_force() const {
+ return query_->options().use_brute_force();
+}
+
+void S2MaxDistanceShapeIndexTarget::set_use_brute_force(
+ bool use_brute_force) {
+ query_->mutable_options()->set_use_brute_force(use_brute_force);
+}
+
+bool S2MaxDistanceShapeIndexTarget::set_max_error(
+ const S1ChordAngle& max_error) {
+ query_->mutable_options()->set_max_error(max_error);
+ return true; // Indicates that we may return suboptimal results.
+}
+
+// This method returns an S2Cap that bounds the antipode of the target. (This
+// is the set of points whose S2MaxDistance to the target is
+// S2MaxDistance::Zero().)
+S2Cap S2MaxDistanceShapeIndexTarget::GetCapBound() {
+ S2Cap cap = MakeS2ShapeIndexRegion(index_).GetCapBound();
+ return S2Cap(-cap.center(), cap.radius());
+}
+
+bool S2MaxDistanceShapeIndexTarget::UpdateMinDistance(
+ const S2Point& p, S2MaxDistance* min_dist) {
+ query_->mutable_options()->set_min_distance(S1ChordAngle(*min_dist));
+ S2FurthestEdgeQuery::PointTarget target(p);
+ S2FurthestEdgeQuery::Result r = query_->FindFurthestEdge(&target);
+ if (r.shape_id() < 0) {
+ return false;
+ }
+ *min_dist = S2MaxDistance(r.distance());
+ return true;
+}
+
+bool S2MaxDistanceShapeIndexTarget::UpdateMinDistance(
+ const S2Point& v0, const S2Point& v1, S2MaxDistance* min_dist) {
+ query_->mutable_options()->set_min_distance(S1ChordAngle(*min_dist));
+ S2FurthestEdgeQuery::EdgeTarget target(v0, v1);
+ S2FurthestEdgeQuery::Result r = query_->FindFurthestEdge(&target);
+ if (r.shape_id() < 0) return false;
+ *min_dist = S2MaxDistance(r.distance());
+ return true;
+}
+
+bool S2MaxDistanceShapeIndexTarget::UpdateMinDistance(
+ const S2Cell& cell, S2MaxDistance* min_dist) {
+ query_->mutable_options()->set_min_distance(S1ChordAngle(*min_dist));
+ S2FurthestEdgeQuery::CellTarget target(cell);
+ S2FurthestEdgeQuery::Result r = query_->FindFurthestEdge(&target);
+ if (r.shape_id() < 0) return false;
+ *min_dist = S2MaxDistance(r.distance());
+ return true;
+}
+
+// For target types consisting of multiple connected components (such as
+// S2MaxDistanceShapeIndexTarget), this method should return the
+// polygons containing the antipodal reflection of *any* connected
+// component. (It is sufficient to test containment of one vertex per
+// connected component, since the API allows us to also return any polygon
+// whose boundary has S2MaxDistance::Zero() to the target.)
+bool S2MaxDistanceShapeIndexTarget::VisitContainingShapes(
+ const S2ShapeIndex& query_index, const ShapeVisitor& visitor) {
+ // It is sufficient to find the set of chain starts in the target index
+ // (i.e., one vertex per connected component of edges) that are contained by
+ // the query index, except for one special case to handle full polygons.
+ //
+ // TODO(ericv): Do this by merge-joining the two S2ShapeIndexes, and share
+ // the code with S2BooleanOperation.
+ for (S2Shape* shape : *index_) {
+ if (shape == nullptr) continue;
+ int num_chains = shape->num_chains();
+ // Shapes that don't have any edges require a special case (below).
+ bool tested_point = false;
+ for (int c = 0; c < num_chains; ++c) {
+ S2Shape::Chain chain = shape->chain(c);
+ if (chain.length == 0) continue;
+ tested_point = true;
+ S2MaxDistancePointTarget target(shape->chain_edge(c, 0).v0);
+ if (!target.VisitContainingShapes(query_index, visitor)) {
+ return false;
+ }
+ }
+ if (!tested_point) {
+ // Special case to handle full polygons.
+ S2Shape::ReferencePoint ref = shape->GetReferencePoint();
+ if (!ref.contained) continue;
+ S2MaxDistancePointTarget target(ref.point);
+ if (!target.VisitContainingShapes(query_index, visitor)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2measures.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "s2/s2pointutil.h"
+#include "s2/s2predicates.h"
+
+// TODO(ericv): Convert to using s2pred::Sign().
+//#include "util/geometry/s2predicates.h"
+
+using std::atan;
+using std::max;
+using std::sqrt;
+using std::tan;
+
+namespace S2 {
+
+double Angle(const S2Point& a, const S2Point& b, const S2Point& c) {
+ // RobustCrossProd() is necessary to get good accuracy when two of the input
+ // points are very close together.
+ return RobustCrossProd(a, b).Angle(RobustCrossProd(c, b));
+}
+
+double TurnAngle(const S2Point& a, const S2Point& b, const S2Point& c) {
+ // We use RobustCrossProd() to get good accuracy when two points are very
+ // close together, and Sign() to ensure that the sign is correct for
+ // turns that are close to 180 degrees.
+ //
+ // Unfortunately we can't save RobustCrossProd(a, b) and pass it as the
+ // optional 4th argument to Sign(), because Sign() requires a.CrossProd(b)
+ // exactly (the robust version differs in magnitude).
+ double angle = RobustCrossProd(a, b).Angle(RobustCrossProd(b, c));
+
+ // Don't return Sign() * angle because it is legal to have (a == c).
+ return (s2pred::Sign(a, b, c) > 0) ? angle : -angle;
+}
+
+double Area(const S2Point& a, const S2Point& b, const S2Point& c) {
+ S2_DCHECK(IsUnitLength(a));
+ S2_DCHECK(IsUnitLength(b));
+ S2_DCHECK(IsUnitLength(c));
+ // This method is based on l'Huilier's theorem,
+ //
+ // tan(E/4) = sqrt(tan(s/2) tan((s-a)/2) tan((s-b)/2) tan((s-c)/2))
+ //
+ // where E is the spherical excess of the triangle (i.e. its area),
+ // a, b, c, are the side lengths, and
+ // s is the semiperimeter (a + b + c) / 2 .
+ //
+ // The only significant source of error using l'Huilier's method is the
+ // cancellation error of the terms (s-a), (s-b), (s-c). This leads to a
+ // *relative* error of about 1e-16 * s / min(s-a, s-b, s-c). This compares
+ // to a relative error of about 1e-15 / E using Girard's formula, where E is
+ // the true area of the triangle. Girard's formula can be even worse than
+ // this for very small triangles, e.g. a triangle with a true area of 1e-30
+ // might evaluate to 1e-5.
+ //
+ // So, we prefer l'Huilier's formula unless dmin < s * (0.1 * E), where
+ // dmin = min(s-a, s-b, s-c). This basically includes all triangles
+ // except for extremely long and skinny ones.
+ //
+ // Since we don't know E, we would like a conservative upper bound on
+ // the triangle area in terms of s and dmin. It's possible to show that
+ // E <= k1 * s * sqrt(s * dmin), where k1 = 2*sqrt(3)/Pi (about 1).
+ // Using this, it's easy to show that we should always use l'Huilier's
+ // method if dmin >= k2 * s^5, where k2 is about 1e-2. Furthermore,
+ // if dmin < k2 * s^5, the triangle area is at most k3 * s^4, where
+ // k3 is about 0.1. Since the best case error using Girard's formula
+ // is about 1e-15, this means that we shouldn't even consider it unless
+ // s >= 3e-4 or so.
+ //
+ // TODO(ericv): Implement rigorous error bounds (analysis already done).
+ double sa = b.Angle(c);
+ double sb = c.Angle(a);
+ double sc = a.Angle(b);
+ double s = 0.5 * (sa + sb + sc);
+ if (s >= 3e-4) {
+ // Consider whether Girard's formula might be more accurate.
+ double s2 = s * s;
+ double dmin = s - max(sa, max(sb, sc));
+ if (dmin < 1e-2 * s * s2 * s2) {
+ // This triangle is skinny enough to consider using Girard's formula.
+ // We increase the area by the approximate maximum error in the Girard
+ // calculation in order to ensure that this test is conservative.
+ double area = GirardArea(a, b, c);
+ if (dmin < s * (0.1 * (area + 5e-15))) return area;
+ }
+ }
+ // Use l'Huilier's formula.
+ return 4 * atan(sqrt(max(0.0, tan(0.5 * s) * tan(0.5 * (s - sa)) *
+ tan(0.5 * (s - sb)) * tan(0.5 * (s - sc)))));
+}
+
+double GirardArea(const S2Point& a, const S2Point& b, const S2Point& c) {
+ // This is equivalent to the usual Girard's formula but is slightly more
+ // accurate, faster to compute, and handles a == b == c without a special
+ // case. RobustCrossProd() is necessary to get good accuracy when two of
+ // the input points are very close together.
+
+ Vector3_d ab = RobustCrossProd(a, b);
+ Vector3_d bc = RobustCrossProd(b, c);
+ Vector3_d ac = RobustCrossProd(a, c);
+ return max(0.0, ab.Angle(ac) - ab.Angle(bc) + bc.Angle(ac));
+}
+
+double SignedArea(const S2Point& a, const S2Point& b, const S2Point& c) {
+ return s2pred::Sign(a, b, c) * Area(a, b, c);
+}
+
+} // namespace S2
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// All of the values below were obtained by a combination of hand analysis and
+// Mathematica. In general, S2_TAN_PROJECTION produces the most uniform
+// shapes and sizes of cells, S2_LINEAR_PROJECTION is considerably worse, and
+// S2_QUADRATIC_PROJECTION is somewhere in between (but generally closer to
+// the tangent projection than the linear one).
+//
+// Note that S2_LINEAR_PROJECTION can be useful for analysis even when another
+// projection is being used, since it allows many cell metrics to be bounded
+// in terms of (u,v) coordinates rather than (s,t) coordinates. (With the
+// linear projection, u = 2 * s - 1 and similarly for v.) Similarly,
+// S2_TAN_PROJECTION allows cell metrics to be bounded in terms of (u,v)
+// coordinate changes when they are measured as distances on the unit sphere.
+
+#include "s2/s2metrics.h"
+
+namespace S2 {
+
+const LengthMetric kMinAngleSpan(
+ S2_PROJECTION == S2_LINEAR_PROJECTION ? 1.0 : // 1.000
+ S2_PROJECTION == S2_TAN_PROJECTION ? M_PI / 2 : // 1.571
+ S2_PROJECTION == S2_QUADRATIC_PROJECTION ? 4. / 3 : // 1.333
+ 0);
+
+const LengthMetric kMaxAngleSpan(
+ S2_PROJECTION == S2_LINEAR_PROJECTION ? 2 : // 2.000
+ S2_PROJECTION == S2_TAN_PROJECTION ? M_PI / 2 : // 1.571
+ S2_PROJECTION == S2_QUADRATIC_PROJECTION ? 1.704897179199218452 : // 1.705
+ 0);
+
+const LengthMetric kAvgAngleSpan(M_PI / 2); // 1.571
+// This is true for all projections.
+
+const LengthMetric kMinWidth(
+ S2_PROJECTION == S2_LINEAR_PROJECTION ? sqrt(2.0 / 3.0) : // 0.816
+ S2_PROJECTION == S2_TAN_PROJECTION ? M_PI / (2 * sqrt(2.0)) : // 1.111
+ S2_PROJECTION == S2_QUADRATIC_PROJECTION ? 2 * sqrt(2.0) / 3 : // 0.943
+ 0);
+
+const LengthMetric kMaxWidth(kMaxAngleSpan.deriv());
+// This is true for all projections.
+
+const LengthMetric kAvgWidth(
+ S2_PROJECTION == S2_LINEAR_PROJECTION ? 1.411459345844456965 : // 1.411
+ S2_PROJECTION == S2_TAN_PROJECTION ? 1.437318638925160885 : // 1.437
+ S2_PROJECTION == S2_QUADRATIC_PROJECTION ? 1.434523672886099389 : // 1.435
+ 0);
+
+const LengthMetric kMinEdge(
+ S2_PROJECTION == S2_LINEAR_PROJECTION ? 2 * sqrt(2.0) / 3 : // 0.943
+ S2_PROJECTION == S2_TAN_PROJECTION ? M_PI / (2 * sqrt(2.0)) : // 1.111
+ S2_PROJECTION == S2_QUADRATIC_PROJECTION ? 2 * sqrt(2.0) / 3 : // 0.943
+ 0);
+
+const LengthMetric kMaxEdge(kMaxAngleSpan.deriv());
+// This is true for all projections.
+
+const LengthMetric kAvgEdge(
+ S2_PROJECTION == S2_LINEAR_PROJECTION ? 1.440034192955603643 : // 1.440
+ S2_PROJECTION == S2_TAN_PROJECTION ? 1.461667032546739266 : // 1.462
+ S2_PROJECTION == S2_QUADRATIC_PROJECTION ? 1.459213746386106062 : // 1.459
+ 0);
+
+const LengthMetric kMinDiag(
+ S2_PROJECTION == S2_LINEAR_PROJECTION ? 2 * sqrt(2.0) / 3 : // 0.943
+ S2_PROJECTION == S2_TAN_PROJECTION ? M_PI * sqrt(2.0) / 3 : // 1.481
+ S2_PROJECTION == S2_QUADRATIC_PROJECTION ? 8 * sqrt(2.0) / 9 : // 1.257
+ 0);
+
+const LengthMetric kMaxDiag(
+ S2_PROJECTION == S2_LINEAR_PROJECTION ? 2 * sqrt(2.0) : // 2.828
+ S2_PROJECTION == S2_TAN_PROJECTION ? M_PI * sqrt(2.0 / 3.0) : // 2.565
+ S2_PROJECTION == S2_QUADRATIC_PROJECTION ? 2.438654594434021032 : // 2.439
+ 0);
+
+const LengthMetric kAvgDiag(
+ S2_PROJECTION == S2_LINEAR_PROJECTION ? 2.031817866418812674 : // 2.032
+ S2_PROJECTION == S2_TAN_PROJECTION ? 2.063623197195635753 : // 2.064
+ S2_PROJECTION == S2_QUADRATIC_PROJECTION ? 2.060422738998471683 : // 2.060
+ 0);
+
+const AreaMetric kMinArea(
+ S2_PROJECTION == S2_LINEAR_PROJECTION ? 4 / (3 * sqrt(3.0)) : // 0.770
+ S2_PROJECTION == S2_TAN_PROJECTION ? (M_PI*M_PI) / (4*sqrt(2.0)) : // 1.745
+ S2_PROJECTION == S2_QUADRATIC_PROJECTION ? 8 * sqrt(2.0) / 9 : // 1.257
+ 0);
+
+const AreaMetric kMaxArea(
+ S2_PROJECTION == S2_LINEAR_PROJECTION ? 4 : // 4.000
+ S2_PROJECTION == S2_TAN_PROJECTION ? M_PI * M_PI / 4 : // 2.467
+ S2_PROJECTION == S2_QUADRATIC_PROJECTION ? 2.635799256963161491 : // 2.636
+ 0);
+
+const AreaMetric kAvgArea(4 * M_PI / 6); // 2.094
+// This is true for all projections.
+
+const double kMaxEdgeAspect = (
+ S2_PROJECTION == S2_LINEAR_PROJECTION ? sqrt(2.0) : // 1.414
+ S2_PROJECTION == S2_TAN_PROJECTION ? sqrt(2.0) : // 1.414
+ S2_PROJECTION == S2_QUADRATIC_PROJECTION ? 1.442615274452682920 : // 1.443
+ 0);
+
+const double kMaxDiagAspect = sqrt(3.0); // 1.732
+// This is true for all projections.
+
+} // namespace S2
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2min_distance_targets.h"
+
+#include <memory>
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/s1angle.h"
+#include "s2/s2cap.h"
+#include "s2/s2cell.h"
+#include "s2/s2closest_cell_query.h"
+#include "s2/s2closest_edge_query.h"
+#include "s2/s2edge_distances.h"
+#include "s2/s2shape_index_region.h"
+
+S2Cap S2MinDistancePointTarget::GetCapBound() {
+ return S2Cap(point_, S1ChordAngle::Zero());
+}
+
+bool S2MinDistancePointTarget::UpdateMinDistance(
+ const S2Point& p, S2MinDistance* min_dist) {
+ return min_dist->UpdateMin(S2MinDistance(S1ChordAngle(p, point_)));
+}
+
+bool S2MinDistancePointTarget::UpdateMinDistance(
+ const S2Point& v0, const S2Point& v1, S2MinDistance* min_dist) {
+ return S2::UpdateMinDistance(point_, v0, v1, min_dist);
+}
+
+bool S2MinDistancePointTarget::UpdateMinDistance(
+ const S2Cell& cell, S2MinDistance* min_dist) {
+ return min_dist->UpdateMin(S2MinDistance(cell.GetDistance(point_)));
+}
+
+bool S2MinDistancePointTarget::VisitContainingShapes(
+ const S2ShapeIndex& index, const ShapeVisitor& visitor) {
+ return MakeS2ContainsPointQuery(&index).VisitContainingShapes(
+ point_, [this, &visitor](S2Shape* shape) {
+ return visitor(shape, point_);
+ });
+}
+
+S2Cap S2MinDistanceEdgeTarget::GetCapBound() {
+ // The following computes a radius equal to half the edge length in an
+ // efficient and numerically stable way.
+ double d2 = S1ChordAngle(a_, b_).length2();
+ double r2 = (0.5 * d2) / (1 + sqrt(1 - 0.25 * d2));
+ return S2Cap((a_ + b_).Normalize(), S1ChordAngle::FromLength2(r2));
+}
+
+bool S2MinDistanceEdgeTarget::UpdateMinDistance(
+ const S2Point& p, S2MinDistance* min_dist) {
+ return S2::UpdateMinDistance(p, a_, b_, min_dist);
+}
+
+bool S2MinDistanceEdgeTarget::UpdateMinDistance(
+ const S2Point& v0, const S2Point& v1, S2MinDistance* min_dist) {
+ return S2::UpdateEdgePairMinDistance(a_, b_, v0, v1, min_dist);
+}
+
+bool S2MinDistanceEdgeTarget::UpdateMinDistance(
+ const S2Cell& cell, S2MinDistance* min_dist) {
+ return min_dist->UpdateMin(S2MinDistance(cell.GetDistance(a_, b_)));
+}
+
+bool S2MinDistanceEdgeTarget::VisitContainingShapes(
+ const S2ShapeIndex& index, const ShapeVisitor& visitor) {
+ // We test the center of the edge in order to ensure that edge targets AB
+ // and BA yield identical results (which is not guaranteed by the API but
+ // users might expect). Other options would be to test both endpoints, or
+ // return different results for AB and BA in some cases.
+ S2MinDistancePointTarget target((a_ + b_).Normalize());
+ return target.VisitContainingShapes(index, visitor);
+}
+
+S2MinDistanceCellTarget::S2MinDistanceCellTarget(const S2Cell& cell)
+ : cell_(cell) {
+}
+
+S2Cap S2MinDistanceCellTarget::GetCapBound() {
+ return cell_.GetCapBound();
+}
+
+bool S2MinDistanceCellTarget::UpdateMinDistance(const S2Point& p,
+ S2MinDistance* min_dist) {
+ return min_dist->UpdateMin(S2MinDistance(cell_.GetDistance(p)));
+}
+
+bool S2MinDistanceCellTarget::UpdateMinDistance(
+ const S2Point& v0, const S2Point& v1, S2MinDistance* min_dist) {
+ return min_dist->UpdateMin(S2MinDistance(cell_.GetDistance(v0, v1)));
+}
+
+bool S2MinDistanceCellTarget::UpdateMinDistance(
+ const S2Cell& cell, S2MinDistance* min_dist) {
+ return min_dist->UpdateMin(S2MinDistance(cell_.GetDistance(cell)));
+}
+
+bool S2MinDistanceCellTarget::VisitContainingShapes(
+ const S2ShapeIndex& index, const ShapeVisitor& visitor) {
+ // The simplest approach is simply to return the polygons that contain the
+ // cell center. Alternatively, if the index cell is smaller than the target
+ // cell then we could return all polygons that are present in the
+ // S2ShapeIndexCell, but since the index is built conservatively this may
+ // include some polygons that don't quite intersect the cell. So we would
+ // either need to recheck for intersection more accurately, or weaken the
+ // VisitContainingShapes contract so that it only guarantees approximate
+ // intersection, neither of which seems like a good tradeoff.
+ S2MinDistancePointTarget target(cell_.GetCenter());
+ return target.VisitContainingShapes(index, visitor);
+}
+
+S2MinDistanceCellUnionTarget::S2MinDistanceCellUnionTarget(
+ S2CellUnion cell_union)
+ : cell_union_(std::move(cell_union)),
+ query_(absl::make_unique<S2ClosestCellQuery>(&index_)) {
+ for (S2CellId cell_id : cell_union_) {
+ index_.Add(cell_id, 0);
+ }
+ index_.Build();
+}
+
+S2MinDistanceCellUnionTarget::~S2MinDistanceCellUnionTarget() {
+}
+
+bool S2MinDistanceCellUnionTarget::use_brute_force() const {
+ return query_->options().use_brute_force();
+}
+
+void S2MinDistanceCellUnionTarget::set_use_brute_force(
+ bool use_brute_force) {
+ query_->mutable_options()->set_use_brute_force(use_brute_force);
+}
+
+bool S2MinDistanceCellUnionTarget::set_max_error(
+ const S1ChordAngle& max_error) {
+ query_->mutable_options()->set_max_error(max_error);
+ return true; // Indicates that we may return suboptimal results.
+}
+
+S2Cap S2MinDistanceCellUnionTarget::GetCapBound() {
+ return cell_union_.GetCapBound();
+}
+
+inline bool S2MinDistanceCellUnionTarget::UpdateMinDistance(
+ S2MinDistanceTarget* target, S2MinDistance* min_dist) {
+ query_->mutable_options()->set_max_distance(*min_dist);
+ S2ClosestCellQuery::Result r = query_->FindClosestCell(target);
+ if (r.is_empty()) return false;
+ *min_dist = r.distance();
+ return true;
+}
+
+bool S2MinDistanceCellUnionTarget::UpdateMinDistance(
+ const S2Point& p, S2MinDistance* min_dist) {
+ S2ClosestCellQuery::PointTarget target(p);
+ return UpdateMinDistance(&target, min_dist);
+}
+
+bool S2MinDistanceCellUnionTarget::UpdateMinDistance(
+ const S2Point& v0, const S2Point& v1, S2MinDistance* min_dist) {
+ S2ClosestCellQuery::EdgeTarget target(v0, v1);
+ return UpdateMinDistance(&target, min_dist);
+}
+
+bool S2MinDistanceCellUnionTarget::UpdateMinDistance(
+ const S2Cell& cell, S2MinDistance* min_dist) {
+ S2ClosestCellQuery::CellTarget target(cell);
+ return UpdateMinDistance(&target, min_dist);
+}
+
+bool S2MinDistanceCellUnionTarget::VisitContainingShapes(
+ const S2ShapeIndex& query_index, const ShapeVisitor& visitor) {
+ for (S2CellId cell_id : cell_union_) {
+ S2MinDistancePointTarget target(cell_id.ToPoint());
+ if (!target.VisitContainingShapes(query_index, visitor)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+S2MinDistanceShapeIndexTarget::S2MinDistanceShapeIndexTarget(
+ const S2ShapeIndex* index)
+ : index_(index), query_(absl::make_unique<S2ClosestEdgeQuery>(index)) {
+}
+
+S2MinDistanceShapeIndexTarget::~S2MinDistanceShapeIndexTarget() {
+}
+
+bool S2MinDistanceShapeIndexTarget::include_interiors() const {
+ return query_->options().include_interiors();
+}
+
+void S2MinDistanceShapeIndexTarget::set_include_interiors(
+ bool include_interiors) {
+ query_->mutable_options()->set_include_interiors(include_interiors);
+}
+
+bool S2MinDistanceShapeIndexTarget::use_brute_force() const {
+ return query_->options().use_brute_force();
+}
+
+void S2MinDistanceShapeIndexTarget::set_use_brute_force(
+ bool use_brute_force) {
+ query_->mutable_options()->set_use_brute_force(use_brute_force);
+}
+
+bool S2MinDistanceShapeIndexTarget::set_max_error(
+ const S1ChordAngle& max_error) {
+ query_->mutable_options()->set_max_error(max_error);
+ return true; // Indicates that we may return suboptimal results.
+}
+
+S2Cap S2MinDistanceShapeIndexTarget::GetCapBound() {
+ return MakeS2ShapeIndexRegion(index_).GetCapBound();
+}
+
+inline bool S2MinDistanceShapeIndexTarget::UpdateMinDistance(
+ S2MinDistanceTarget* target, S2MinDistance* min_dist) {
+ query_->mutable_options()->set_max_distance(*min_dist);
+ S2ClosestEdgeQuery::Result r = query_->FindClosestEdge(target);
+ if (r.is_empty()) return false;
+ *min_dist = r.distance();
+ return true;
+}
+
+bool S2MinDistanceShapeIndexTarget::UpdateMinDistance(
+ const S2Point& p, S2MinDistance* min_dist) {
+ S2ClosestEdgeQuery::PointTarget target(p);
+ return UpdateMinDistance(&target, min_dist);
+}
+
+bool S2MinDistanceShapeIndexTarget::UpdateMinDistance(
+ const S2Point& v0, const S2Point& v1, S2MinDistance* min_dist) {
+ S2ClosestEdgeQuery::EdgeTarget target(v0, v1);
+ return UpdateMinDistance(&target, min_dist);
+}
+
+bool S2MinDistanceShapeIndexTarget::UpdateMinDistance(
+ const S2Cell& cell, S2MinDistance* min_dist) {
+ S2ClosestEdgeQuery::CellTarget target(cell);
+ return UpdateMinDistance(&target, min_dist);
+}
+
+bool S2MinDistanceShapeIndexTarget::VisitContainingShapes(
+ const S2ShapeIndex& query_index, const ShapeVisitor& visitor) {
+ // It is sufficient to find the set of chain starts in the target index
+ // (i.e., one vertex per connected component of edges) that are contained by
+ // the query index, except for one special case to handle full polygons.
+ //
+ // TODO(ericv): Do this by merge-joining the two S2ShapeIndexes, and share
+ // the code with S2BooleanOperation.
+
+ for (S2Shape* shape : *index_) {
+ if (shape == nullptr) continue;
+ int num_chains = shape->num_chains();
+ // Shapes that don't have any edges require a special case (below).
+ bool tested_point = false;
+ for (int c = 0; c < num_chains; ++c) {
+ S2Shape::Chain chain = shape->chain(c);
+ if (chain.length == 0) continue;
+ tested_point = true;
+ S2Point v0 = shape->chain_edge(c, 0).v0;
+ S2MinDistancePointTarget target(v0);
+ if (!target.VisitContainingShapes(query_index, visitor)) {
+ return false;
+ }
+ }
+ if (!tested_point) {
+ // Special case to handle full polygons.
+ S2Shape::ReferencePoint ref = shape->GetReferencePoint();
+ if (!ref.contained) continue;
+ S2MinDistancePointTarget target(ref.point);
+ if (!target.VisitContainingShapes(query_index, visitor)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2padded_cell.h"
+
+#include <algorithm>
+#include <cfloat>
+
+#include "s2/util/bits/bits.h"
+#include "s2/r1interval.h"
+#include "s2/s2coords.h"
+
+using std::max;
+using std::min;
+using S2::internal::kSwapMask;
+using S2::internal::kInvertMask;
+using S2::internal::kIJtoPos;
+using S2::internal::kPosToOrientation;
+
+S2PaddedCell::S2PaddedCell(S2CellId id, double padding)
+ : id_(id), padding_(padding) {
+ if (id_.is_face()) {
+ // Fast path for constructing a top-level face (the most common case).
+ double limit = 1 + padding;
+ bound_ = R2Rect(R1Interval(-limit, limit), R1Interval(-limit, limit));
+ middle_ = R2Rect(R1Interval(-padding, padding),
+ R1Interval(-padding, padding));
+ ij_lo_[0] = ij_lo_[1] = 0;
+ orientation_ = id_.face() & 1;
+ level_ = 0;
+ } else {
+ int ij[2];
+ id.ToFaceIJOrientation(&ij[0], &ij[1], &orientation_);
+ level_ = id.level();
+ bound_ = S2CellId::IJLevelToBoundUV(ij, level_).Expanded(padding);
+ int ij_size = S2CellId::GetSizeIJ(level_);
+ ij_lo_[0] = ij[0] & -ij_size;
+ ij_lo_[1] = ij[1] & -ij_size;
+ }
+}
+
+S2PaddedCell::S2PaddedCell(const S2PaddedCell& parent, int i, int j)
+ : padding_(parent.padding_),
+ bound_(parent.bound_),
+ level_(parent.level_ + 1) {
+ // Compute the position and orientation of the child incrementally from the
+ // orientation of the parent.
+ int pos = kIJtoPos[parent.orientation_][2*i+j];
+ id_ = parent.id_.child(pos);
+ int ij_size = S2CellId::GetSizeIJ(level_);
+ ij_lo_[0] = parent.ij_lo_[0] + i * ij_size;
+ ij_lo_[1] = parent.ij_lo_[1] + j * ij_size;
+ orientation_ = parent.orientation_ ^ kPosToOrientation[pos];
+ // For each child, one corner of the bound is taken directly from the parent
+ // while the diagonally opposite corner is taken from middle().
+ const R2Rect& middle = parent.middle();
+ bound_[0][1-i] = middle[0][1-i];
+ bound_[1][1-j] = middle[1][1-j];
+}
+
+const R2Rect& S2PaddedCell::middle() const {
+ // We compute this field lazily because it is not needed the majority of the
+ // time (i.e., for cells where the recursion terminates).
+ if (middle_.is_empty()) {
+ int ij_size = S2CellId::GetSizeIJ(level_);
+ double u = S2::STtoUV(S2::SiTitoST(2 * ij_lo_[0] + ij_size));
+ double v = S2::STtoUV(S2::SiTitoST(2 * ij_lo_[1] + ij_size));
+ middle_ = R2Rect(R1Interval(u - padding_, u + padding_),
+ R1Interval(v - padding_, v + padding_));
+ }
+ return middle_;
+}
+
+S2Point S2PaddedCell::GetCenter() const {
+ int ij_size = S2CellId::GetSizeIJ(level_);
+ unsigned int si = 2 * ij_lo_[0] + ij_size;
+ unsigned int ti = 2 * ij_lo_[1] + ij_size;
+ return S2::FaceSiTitoXYZ(id_.face(), si, ti).Normalize();
+}
+
+S2Point S2PaddedCell::GetEntryVertex() const {
+ // The curve enters at the (0,0) vertex unless the axis directions are
+ // reversed, in which case it enters at the (1,1) vertex.
+ unsigned int i = ij_lo_[0];
+ unsigned int j = ij_lo_[1];
+ if (orientation_ & kInvertMask) {
+ int ij_size = S2CellId::GetSizeIJ(level_);
+ i += ij_size;
+ j += ij_size;
+ }
+ return S2::FaceSiTitoXYZ(id_.face(), 2 * i, 2 * j).Normalize();
+}
+
+S2Point S2PaddedCell::GetExitVertex() const {
+ // The curve exits at the (1,0) vertex unless the axes are swapped or
+ // inverted but not both, in which case it exits at the (0,1) vertex.
+ unsigned int i = ij_lo_[0];
+ unsigned int j = ij_lo_[1];
+ int ij_size = S2CellId::GetSizeIJ(level_);
+ if (orientation_ == 0 || orientation_ == kSwapMask + kInvertMask) {
+ i += ij_size;
+ } else {
+ j += ij_size;
+ }
+ return S2::FaceSiTitoXYZ(id_.face(), 2 * i, 2 * j).Normalize();
+}
+
+S2CellId S2PaddedCell::ShrinkToFit(const R2Rect& rect) const {
+ S2_DCHECK(bound().Intersects(rect));
+
+ // Quick rejection test: if "rect" contains the center of this cell along
+ // either axis, then no further shrinking is possible.
+ int ij_size = S2CellId::GetSizeIJ(level_);
+ if (level_ == 0) {
+ // Fast path (most calls to this function start with a face cell).
+ if (rect[0].Contains(0) || rect[1].Contains(0)) return id();
+ } else {
+ if (rect[0].Contains(S2::STtoUV(S2::SiTitoST(2 * ij_lo_[0] + ij_size))) ||
+ rect[1].Contains(S2::STtoUV(S2::SiTitoST(2 * ij_lo_[1] + ij_size)))) {
+ return id();
+ }
+ }
+ // Otherwise we expand "rect" by the given padding() on all sides and find
+ // the range of coordinates that it spans along the i- and j-axes. We then
+ // compute the highest bit position at which the min and max coordinates
+ // differ. This corresponds to the first cell level at which at least two
+ // children intersect "rect".
+
+ // Increase the padding to compensate for the error in S2::UVtoST().
+ // (The constant below is a provable upper bound on the additional error.)
+ R2Rect padded = rect.Expanded(padding() + 1.5 * DBL_EPSILON);
+ int ij_min[2]; // Min i- or j- coordinate spanned by "padded"
+ int ij_xor[2]; // XOR of the min and max i- or j-coordinates
+ for (int d = 0; d < 2; ++d) {
+ ij_min[d] = max(ij_lo_[d], S2::STtoIJ(S2::UVtoST(padded[d][0])));
+ int ij_max = min(ij_lo_[d] + ij_size - 1,
+ S2::STtoIJ(S2::UVtoST(padded[d][1])));
+ ij_xor[d] = ij_min[d] ^ ij_max;
+ }
+ // Compute the highest bit position where the two i- or j-endpoints differ,
+ // and then choose the cell level that includes both of these endpoints. So
+ // if both pairs of endpoints are equal we choose kMaxLevel; if they differ
+ // only at bit 0, we choose (kMaxLevel - 1), and so on.
+ int level_msb = ((ij_xor[0] | ij_xor[1]) << 1) + 1;
+ int level = S2CellId::kMaxLevel - Bits::FindMSBSetNonZero(level_msb);
+ if (level <= level_) return id();
+ return S2CellId::FromFaceIJ(id().face(), ij_min[0], ij_min[1]).parent(level);
+}
--- /dev/null
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+
+#include "s2/s2point_compression.h"
+
+#include <utility>
+#include <vector>
+
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2coords.h"
+#include "s2/third_party/absl/base/casts.h"
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/third_party/absl/container/fixed_array.h"
+#include "s2/third_party/absl/types/span.h"
+#include "s2/util/bits/bit-interleave.h"
+#include "s2/util/coding/coder.h"
+#include "s2/util/coding/nth-derivative.h"
+#include "s2/util/coding/transforms.h"
+#include "s2/util/endian/endian.h"
+
+using absl::Span;
+using std::pair;
+using std::vector;
+
+namespace {
+
+const int kDerivativeEncodingOrder = 2;
+
+// Pair of face number and count for run-length encoding.
+struct FaceRun {
+ FaceRun() : face(-1), count(0) {}
+ FaceRun(int initial_face, int initial_count)
+ : face(initial_face), count(initial_count) {}
+
+ // Encodes each face as a varint64 with value kNumFaces * count + face.
+ // 21 faces can fit in a single byte. Varint64 is used so that 4G faces
+ // can be encoded instead of just 4G / 6 = ~700M.
+ void Encode(Encoder* encoder) const {
+ encoder->Ensure(Encoder::kVarintMax64);
+
+ // It isn't necessary to encode the number of faces left for the last run,
+ // but since this would only help if there were more than 21 faces, it will
+ // be a small overall savings, much smaller than the bound encoding.
+ encoder->put_varint64(
+ S2CellId::kNumFaces * absl::implicit_cast<int64>(count) + face);
+ S2_DCHECK_GE(encoder->avail(), 0);
+ }
+
+ bool Decode(Decoder* decoder) {
+ uint64 face_and_count;
+ if (!decoder->get_varint64(&face_and_count)) return false;
+
+ face = face_and_count % S2CellId::kNumFaces;
+ // Make sure large counts don't wrap on malicious or random input.
+ const uint64 count64 = face_and_count / S2CellId::kNumFaces;
+ count = count64;
+
+ return count > 0 && count == count64;
+ }
+
+ int face;
+ int count;
+};
+
+// Run-length encoder/decoder for face numbers.
+class Faces {
+ public:
+ class Iterator {
+ public:
+ explicit Iterator(const Faces& faces);
+
+ // Return the next face.
+ int Next();
+
+ private:
+ // The faces_ vector of the Faces object for which this is an iterator.
+ const vector<FaceRun>& faces_;
+
+ // The index that the next face will come from.
+ int face_index_;
+
+ // Number of faces already consumed for face_index_.
+ int num_faces_used_for_index_;
+ };
+
+ Faces() {}
+
+ // Add the face to the list of face runs, combining with the last if
+ // possible.
+ void AddFace(int face);
+
+ // Encodes the faces to encoder.
+ void Encode(Encoder* encoder) const;
+
+ // Decodes the faces, returning true on success.
+ bool Decode(int num_vertices, Decoder* decoder);
+
+ Iterator GetIterator() const {
+ Iterator iterator(*this);
+ return iterator;
+ }
+
+ private:
+ // Run-length encoded list of faces.
+ vector<FaceRun> faces_;
+
+ Faces(const Faces&) = delete;
+ void operator=(const Faces&) = delete;
+};
+
+void Faces::AddFace(int face) {
+ if (!faces_.empty() && faces_.back().face == face) {
+ ++faces_.back().count;
+ } else {
+ faces_.push_back(FaceRun(face, 1));
+ }
+}
+
+void Faces::Encode(Encoder* encoder) const {
+ for (const FaceRun& face_run : faces_)
+ face_run.Encode(encoder);
+}
+
+bool Faces::Decode(int num_vertices, Decoder* decoder) {
+ for (int num_faces_parsed = 0; num_faces_parsed < num_vertices; ) {
+ FaceRun face_run;
+ if (!face_run.Decode(decoder)) return false;
+ faces_.push_back(face_run);
+
+ num_faces_parsed += face_run.count;
+ }
+
+ return true;
+}
+
+Faces::Iterator::Iterator(const Faces& faces)
+ : faces_(faces.faces_), face_index_(0),
+ num_faces_used_for_index_(0) {
+}
+
+int Faces::Iterator::Next() {
+ S2_DCHECK_NE(faces_.size(), face_index_);
+ S2_DCHECK_LE(num_faces_used_for_index_, faces_[face_index_].count);
+ if (num_faces_used_for_index_ == faces_[face_index_].count) {
+ ++face_index_;
+ num_faces_used_for_index_ = 0;
+ }
+
+ ++num_faces_used_for_index_;
+ return faces_[face_index_].face;
+}
+
+// Unused function (for documentation purposes only).
+inline int STtoPiQi(double s, int level) {
+ // We introduce a new coordinate system (pi, qi), which is (si, ti)
+ // with the bits that are constant for cells of that level shifted
+ // off to the right.
+ // si = round(s * 2^31)
+ // pi = si >> (31 - level)
+ // = floor(s * 2^level)
+ // If the point has been snapped to the level, the bits that are
+ // shifted off will be a 1 in the msb, then 0s after that, so the
+ // fractional part discarded by the cast is (close to) 0.5.
+ return static_cast<int>(s * (1 << level));
+}
+
+inline int SiTitoPiQi(unsigned int si, int level) {
+ // See STtoPiQi for the definition of the PiQi coordinate system.
+ //
+ // EncodeFirstPointFixedLength encodes the return value using "level" bits,
+ // so we clamp "si" to the range [0, 2**level - 1] before trying to encode
+ // it. This is okay because if si == kMaxSiTi, then it is not a cell center
+ // anyway and will be encoded separately as an "off-center" point.
+ si = std::min(si, S2::kMaxSiTi - 1);
+ return si >> (S2::kMaxCellLevel + 1 - level);
+}
+
+inline double PiQitoST(int pi, int level) {
+ // We want to recover the position at the center of the cell. If the point
+ // was snapped to the center of the cell, then modf(s * 2^level) == 0.5.
+ // Inverting STtoPiQi gives:
+ // s = (pi + 0.5) / 2^level.
+ return (pi + 0.5) / (1 << level);
+}
+
+S2Point FacePiQitoXYZ(int face, int pi, int qi, int level) {
+ return S2::FaceUVtoXYZ(face,
+ S2::STtoUV(PiQitoST(pi, level)),
+ S2::STtoUV(PiQitoST(qi, level))).Normalize();
+}
+
+void EncodeFirstPointFixedLength(const pair<int, int>& vertex_pi_qi,
+ int level,
+ NthDerivativeCoder* pi_coder,
+ NthDerivativeCoder* qi_coder,
+ Encoder* encoder) {
+ // Do not ZigZagEncode the first point, since it cannot be negative.
+ const uint32 pi = pi_coder->Encode(vertex_pi_qi.first);
+ const uint32 qi = qi_coder->Encode(vertex_pi_qi.second);
+ // Interleave to reduce overhead from two partial bytes to one.
+ const uint64 interleaved_pi_qi = util_bits::InterleaveUint32(pi, qi);
+
+ // Convert to little endian for architecture independence.
+ const uint64 little_endian_interleaved_pi_qi =
+ LittleEndian::FromHost64(interleaved_pi_qi);
+
+ const int bytes_required = (level + 7) / 8 * 2;
+ encoder->Ensure(bytes_required);
+ encoder->putn(&little_endian_interleaved_pi_qi, bytes_required);
+ S2_DCHECK_GE(encoder->avail(), 0);
+}
+
+void EncodePointCompressed(const pair<int, int>& vertex_pi_qi,
+ int level,
+ NthDerivativeCoder* pi_coder,
+ NthDerivativeCoder* qi_coder,
+ Encoder* encoder) {
+ // ZigZagEncode, as varint requires the maximum number of bytes for
+ // negative numbers.
+ const uint32 zig_zag_encoded_deriv_pi =
+ ZigZagEncode(pi_coder->Encode(vertex_pi_qi.first));
+ const uint32 zig_zag_encoded_deriv_qi =
+ ZigZagEncode(qi_coder->Encode(vertex_pi_qi.second));
+ // Interleave to reduce overhead from two partial bytes to one.
+ const uint64 interleaved_zig_zag_encoded_derivs =
+ util_bits::InterleaveUint32(zig_zag_encoded_deriv_pi,
+ zig_zag_encoded_deriv_qi);
+
+ encoder->Ensure(Encoder::kVarintMax64);
+ encoder->put_varint64(interleaved_zig_zag_encoded_derivs);
+ S2_DCHECK_GE(encoder->avail(), 0);
+}
+
+void EncodePointsCompressed(Span<const pair<int, int>> vertices_pi_qi,
+ int level, Encoder* encoder) {
+ NthDerivativeCoder pi_coder(kDerivativeEncodingOrder);
+ NthDerivativeCoder qi_coder(kDerivativeEncodingOrder);
+ for (int i = 0; i < vertices_pi_qi.size(); ++i) {
+ if (i == 0) {
+ // The first point will be just the (pi, qi) coordinates
+ // of the S2Point. NthDerivativeCoder will not save anything
+ // in that case, so we encode in fixed format rather than varint
+ // to avoid the varint overhead.
+ EncodeFirstPointFixedLength(vertices_pi_qi[i], level,
+ &pi_coder, &qi_coder, encoder);
+ } else {
+ EncodePointCompressed(vertices_pi_qi[i], level,
+ &pi_coder, &qi_coder, encoder);
+ }
+ }
+
+ S2_DCHECK_GE(encoder->avail(), 0);
+}
+
+bool DecodeFirstPointFixedLength(Decoder* decoder,
+ int level,
+ NthDerivativeCoder* pi_coder,
+ NthDerivativeCoder* qi_coder,
+ pair<int, int>* vertex_pi_qi) {
+ const int bytes_required = (level + 7) / 8 * 2;
+ if (decoder->avail() < bytes_required) return false;
+ uint64 little_endian_interleaved_pi_qi = 0;
+ decoder->getn(&little_endian_interleaved_pi_qi, bytes_required);
+
+ const uint64 interleaved_pi_qi =
+ LittleEndian::ToHost64(little_endian_interleaved_pi_qi);
+
+ uint32 pi, qi;
+ util_bits::DeinterleaveUint32(interleaved_pi_qi, &pi, &qi);
+
+ vertex_pi_qi->first = pi_coder->Decode(pi);
+ vertex_pi_qi->second = qi_coder->Decode(qi);
+
+ return true;
+}
+
+bool DecodePointCompressed(Decoder* decoder,
+ int level,
+ NthDerivativeCoder* pi_coder,
+ NthDerivativeCoder* qi_coder,
+ pair<int, int>* vertex_pi_qi) {
+ uint64 interleaved_zig_zag_encoded_deriv_pi_qi;
+ if (!decoder->get_varint64(&interleaved_zig_zag_encoded_deriv_pi_qi)) {
+ return false;
+ }
+
+ uint32 zig_zag_encoded_deriv_pi, zig_zag_encoded_deriv_qi;
+ util_bits::DeinterleaveUint32(interleaved_zig_zag_encoded_deriv_pi_qi,
+ &zig_zag_encoded_deriv_pi,
+ &zig_zag_encoded_deriv_qi);
+
+ vertex_pi_qi->first =
+ pi_coder->Decode(ZigZagDecode(zig_zag_encoded_deriv_pi));
+ vertex_pi_qi->second =
+ qi_coder->Decode(ZigZagDecode(zig_zag_encoded_deriv_qi));
+
+ return true;
+}
+
+} // namespace
+
+void S2EncodePointsCompressed(Span<const S2XYZFaceSiTi> points,
+ int level,
+ Encoder* encoder) {
+ absl::FixedArray<pair<int, int>> vertices_pi_qi(points.size());
+ vector<int> off_center;
+ Faces faces;
+ for (int i = 0; i < points.size(); ++i) {
+ faces.AddFace(points[i].face);
+ vertices_pi_qi[i].first = SiTitoPiQi(points[i].si, level);
+ vertices_pi_qi[i].second = SiTitoPiQi(points[i].ti, level);
+ if (points[i].cell_level != level) {
+ off_center.push_back(i);
+ }
+ }
+ faces.Encode(encoder);
+ EncodePointsCompressed(vertices_pi_qi, level, encoder);
+ int num_off_center = off_center.size();
+ encoder->Ensure(Encoder::kVarintMax32 +
+ (Encoder::kVarintMax32 + sizeof(S2Point)) * num_off_center);
+ encoder->put_varint32(num_off_center);
+ S2_DCHECK_GE(encoder->avail(), 0);
+ for (int index : off_center) {
+ encoder->put_varint32(index);
+ encoder->putn(&points[index].xyz, sizeof(points[index].xyz));
+ S2_DCHECK_GE(encoder->avail(), 0);
+ }
+}
+
+bool S2DecodePointsCompressed(Decoder* decoder, int level,
+ Span<S2Point> points) {
+ Faces faces;
+ if (!faces.Decode(points.size(), decoder)) {
+ return false;
+ }
+
+ NthDerivativeCoder pi_coder(kDerivativeEncodingOrder);
+ NthDerivativeCoder qi_coder(kDerivativeEncodingOrder);
+ Faces::Iterator faces_iterator = faces.GetIterator();
+ for (int i = 0; i < points.size(); ++i) {
+ pair<int, int> vertex_pi_qi;
+ if (i == 0) {
+ if (!DecodeFirstPointFixedLength(decoder, level, &pi_coder, &qi_coder,
+ &vertex_pi_qi)) {
+ return false;
+ }
+ } else {
+ if (!DecodePointCompressed(decoder, level, &pi_coder, &qi_coder,
+ &vertex_pi_qi)) {
+ return false;
+ }
+ }
+
+ int face = faces_iterator.Next();
+ points[i] =
+ FacePiQitoXYZ(face, vertex_pi_qi.first, vertex_pi_qi.second, level);
+ }
+
+ unsigned int num_off_center;
+ if (!decoder->get_varint32(&num_off_center) ||
+ num_off_center > points.size()) {
+ return false;
+ }
+ for (int i = 0; i < num_off_center; ++i) {
+ uint32 index;
+ if (!decoder->get_varint32(&index) || index >= points.size()) {
+ return false;
+ }
+ if (decoder->avail() < sizeof(points[index])) return false;
+ decoder->getn(&points[index], sizeof(points[index]));
+ }
+ return true;
+}
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2point_region.h"
+
+#include "s2/base/logging.h"
+#include "s2/util/coding/coder.h"
+#include "s2/s2cap.h"
+#include "s2/s2cell.h"
+#include "s2/s2latlng.h"
+#include "s2/s2latlng_rect.h"
+#include "s2/s2pointutil.h"
+
+static const unsigned char kCurrentLosslessEncodingVersionNumber = 1;
+
+S2PointRegion::~S2PointRegion() {
+}
+
+S2PointRegion* S2PointRegion::Clone() const {
+ return new S2PointRegion(point_);
+}
+
+S2Cap S2PointRegion::GetCapBound() const {
+ return S2Cap::FromPoint(point_);
+}
+
+S2LatLngRect S2PointRegion::GetRectBound() const {
+ S2LatLng ll(point_);
+ return S2LatLngRect(ll, ll);
+}
+
+bool S2PointRegion::MayIntersect(const S2Cell& cell) const {
+ return cell.Contains(point_);
+}
+
+void S2PointRegion::Encode(Encoder* encoder) const {
+ encoder->Ensure(30); // sufficient
+
+ encoder->put8(kCurrentLosslessEncodingVersionNumber);
+ for (int i = 0; i < 3; ++i) {
+ encoder->putdouble(point_[i]);
+ }
+ S2_DCHECK_GE(encoder->avail(), 0);
+}
+
+bool S2PointRegion::Decode(Decoder* decoder) {
+ if (decoder->avail() < sizeof(unsigned char) + 3 * sizeof(double))
+ return false;
+ unsigned char version = decoder->get8();
+ if (version > kCurrentLosslessEncodingVersionNumber) return false;
+
+ for (int i = 0; i < 3; ++i) {
+ point_[i] = decoder->getdouble();
+ }
+ if (!S2::IsUnitLength(point_)) return false;
+
+ return true;
+}
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2pointutil.h"
+
+#include <cfloat>
+#include <cmath>
+
+using std::fabs;
+
+namespace S2 {
+
+bool IsUnitLength(const S2Point& p) {
+ // Normalize() is guaranteed to return a vector whose L2-norm differs from 1
+ // by less than 2 * DBL_EPSILON. Thus the squared L2-norm differs by less
+ // than 4 * DBL_EPSILON. The actual calculated Norm2() can have up to 1.5 *
+ // DBL_EPSILON of additional error. The total error of 5.5 * DBL_EPSILON
+ // can then be rounded down since the result must be a representable
+ // double-precision value.
+ return fabs(p.Norm2() - 1) <= 5 * DBL_EPSILON; // About 1.11e-15
+}
+
+bool ApproxEquals(const S2Point& a, const S2Point& b, S1Angle max_error) {
+ S2_DCHECK_NE(a, S2Point());
+ S2_DCHECK_NE(b, S2Point());
+ return S1Angle(a, b) <= max_error;
+}
+
+S2Point Ortho(const S2Point& a) {
+#ifdef S2_TEST_DEGENERACIES
+ // Vector3::Ortho() always returns a point on the X-Y, Y-Z, or X-Z planes.
+ // This leads to many more degenerate cases in polygon operations.
+ return a.Ortho();
+#else
+ int k = a.LargestAbsComponent() - 1;
+ if (k < 0) k = 2;
+ S2Point temp(0.012, 0.0053, 0.00457);
+ temp[k] = 1;
+ return a.CrossProd(temp).Normalize();
+#endif
+}
+
+Vector3_d RobustCrossProd(const S2Point& a, const S2Point& b) {
+ // The direction of a.CrossProd(b) becomes unstable as (a + b) or (a - b)
+ // approaches zero. This leads to situations where a.CrossProd(b) is not
+ // very orthogonal to "a" and/or "b". We could fix this using Gram-Schmidt,
+ // but we also want b.RobustCrossProd(a) == -a.RobustCrossProd(b).
+ //
+ // The easiest fix is to just compute the cross product of (b+a) and (b-a).
+ // Mathematically, this cross product is exactly twice the cross product of
+ // "a" and "b", but it has the numerical advantage that (b+a) and (b-a)
+ // are always perpendicular (since "a" and "b" are unit length). This
+ // yields a result that is nearly orthogonal to both "a" and "b" even if
+ // these two values differ only in the lowest bit of one component.
+
+ S2_DCHECK(IsUnitLength(a));
+ S2_DCHECK(IsUnitLength(b));
+ Vector3_d x = (b + a).CrossProd(b - a);
+ if (x != S2Point(0, 0, 0)) return x;
+
+ // The only result that makes sense mathematically is to return zero, but
+ // we find it more convenient to return an arbitrary orthogonal vector.
+ return Ortho(a);
+}
+
+S2Point Rotate(const S2Point& p, const S2Point& axis, S1Angle angle) {
+ S2_DCHECK(IsUnitLength(p));
+ S2_DCHECK(IsUnitLength(axis));
+ // Let M be the plane through P that is perpendicular to "axis", and let
+ // "center" be the point where M intersects "axis". We construct a
+ // right-handed orthogonal frame (dx, dy, center) such that "dx" is the
+ // vector from "center" to P, and "dy" has the same length as "dx". The
+ // result can then be expressed as (cos(angle)*dx + sin(angle)*dy + center).
+ S2Point center = p.DotProd(axis) * axis;
+ S2Point dx = p - center;
+ S2Point dy = axis.CrossProd(p);
+ // Mathematically the result is unit length, but normalization is necessary
+ // to ensure that numerical errors don't accumulate.
+ return (cos(angle) * dx + sin(angle) * dy + center).Normalize();
+}
+
+Matrix3x3_d GetFrame(const S2Point& z) {
+ Matrix3x3_d m;
+ GetFrame(z, &m);
+ return m;
+}
+
+void GetFrame(const S2Point& z, Matrix3x3_d* m) {
+ S2_DCHECK(IsUnitLength(z));
+ m->SetCol(2, z);
+ m->SetCol(1, Ortho(z));
+ m->SetCol(0, m->Col(1).CrossProd(z)); // Already unit-length.
+}
+
+S2Point ToFrame(const Matrix3x3_d& m, const S2Point& p) {
+ // The inverse of an orthonormal matrix is its transpose.
+ return m.Transpose() * p;
+}
+
+S2Point FromFrame(const Matrix3x3_d& m, const S2Point& q) {
+ return m * q;
+}
+
+bool SimpleCCW(const S2Point& a, const S2Point& b, const S2Point& c) {
+ // We compute the signed volume of the parallelepiped ABC. The usual
+ // formula for this is (AxB).C, but we compute it here using (CxA).B
+ // in order to ensure that ABC and CBA are not both CCW. This follows
+ // from the following identities (which are true numerically, not just
+ // mathematically):
+ //
+ // (1) x.CrossProd(y) == -(y.CrossProd(x))
+ // (2) (-x).DotProd(y) == -(x.DotProd(y))
+
+ return c.CrossProd(a).DotProd(b) > 0;
+}
+
+} // namespace S2
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2polygon.h"
+
+#include <algorithm>
+#include <array>
+#include <cmath>
+#include <cstddef>
+#include <set>
+#include <stack>
+#include <utility>
+#include <vector>
+
+
+#include "s2/base/casts.h"
+#include "s2/base/commandlineflags.h"
+#include "s2/base/logging.h"
+#include "s2/mutable_s2shape_index.h"
+#include "s2/s1angle.h"
+#include "s2/s1interval.h"
+#include "s2/s2boolean_operation.h"
+#include "s2/s2builder.h"
+#include "s2/s2builderutil_s2polygon_layer.h"
+#include "s2/s2builderutil_s2polyline_layer.h"
+#include "s2/s2builderutil_s2polyline_vector_layer.h"
+#include "s2/s2builderutil_snap_functions.h"
+#include "s2/s2cap.h"
+#include "s2/s2cell.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2cell_union.h"
+#include "s2/s2closest_edge_query.h"
+#include "s2/s2contains_point_query.h"
+#include "s2/s2coords.h"
+#include "s2/s2crossing_edge_query.h"
+#include "s2/s2debug.h"
+#include "s2/s2edge_clipping.h"
+#include "s2/s2edge_crosser.h"
+#include "s2/s2edge_crossings.h"
+#include "s2/s2error.h"
+#include "s2/s2latlng.h"
+#include "s2/s2latlng_rect.h"
+#include "s2/s2latlng_rect_bounder.h"
+#include "s2/s2loop.h"
+#include "s2/s2measures.h"
+#include "s2/s2metrics.h"
+#include "s2/s2point_compression.h"
+#include "s2/s2polyline.h"
+#include "s2/s2predicates.h"
+#include "s2/s2shape_index.h"
+#include "s2/s2shape_index_region.h"
+#include "s2/s2shapeutil_visit_crossing_edge_pairs.h"
+#include "s2/third_party/absl/container/fixed_array.h"
+#include "s2/third_party/absl/container/inlined_vector.h"
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/util/coding/coder.h"
+
+using absl::make_unique;
+using s2builderutil::IdentitySnapFunction;
+using s2builderutil::S2PolygonLayer;
+using s2builderutil::S2PolylineLayer;
+using s2builderutil::S2PolylineVectorLayer;
+using s2builderutil::S2CellIdSnapFunction;
+using std::fabs;
+using std::max;
+using std::min;
+using std::pair;
+using std::set;
+using std::sqrt;
+using std::unique_ptr;
+using std::vector;
+
+DEFINE_bool(
+ s2polygon_lazy_indexing, true,
+ "Build the S2ShapeIndex only when it is first needed. This can save "
+ "significant amounts of memory and time when geometry is constructed but "
+ "never queried, for example when converting from one format to another.");
+
+// The maximum number of loops we'll allow when decoding a polygon.
+// The default value of 10 million is 200x bigger than the number of
+DEFINE_int32(
+ s2polygon_decode_max_num_loops, 10000000,
+ "The upper limit on the number of loops that are allowed by the "
+ "S2Polygon::Decode method.");
+
+// When adding a new encoding, be aware that old binaries will not
+// be able to decode it.
+static const unsigned char kCurrentUncompressedEncodingVersionNumber = 1;
+static const unsigned char kCurrentCompressedEncodingVersionNumber = 4;
+
+S2Polygon::S2Polygon()
+ : s2debug_override_(S2Debug::ALLOW),
+ error_inconsistent_loop_orientations_(false),
+ num_vertices_(0),
+ unindexed_contains_calls_(0) {
+}
+
+S2Polygon::S2Polygon(vector<unique_ptr<S2Loop>> loops, S2Debug override)
+ : s2debug_override_(override) {
+ InitNested(std::move(loops));
+}
+
+S2Polygon::S2Polygon(unique_ptr<S2Loop> loop, S2Debug override)
+ : s2debug_override_(override) {
+ Init(std::move(loop));
+}
+
+S2Polygon::S2Polygon(const S2Cell& cell)
+ : s2debug_override_(S2Debug::ALLOW) {
+ Init(make_unique<S2Loop>(cell));
+}
+
+void S2Polygon::set_s2debug_override(S2Debug override) {
+ s2debug_override_ = override;
+}
+
+S2Debug S2Polygon::s2debug_override() const {
+ return s2debug_override_;
+}
+
+void S2Polygon::Copy(const S2Polygon* src) {
+ ClearLoops();
+ for (int i = 0; i < src->num_loops(); ++i) {
+ loops_.emplace_back(src->loop(i)->Clone());
+ }
+ s2debug_override_ = src->s2debug_override_;
+ // Don't copy error_inconsistent_loop_orientations_, since this is not a
+ // property of the polygon but only of the way the polygon was constructed.
+ num_vertices_ = src->num_vertices();
+ unindexed_contains_calls_.store(0, std::memory_order_relaxed);
+ bound_ = src->bound_;
+ subregion_bound_ = src->subregion_bound_;
+ InitIndex(); // TODO(ericv): Copy the index efficiently.
+}
+
+S2Polygon* S2Polygon::Clone() const {
+ S2Polygon* result = new S2Polygon;
+ result->Copy(this);
+ return result;
+}
+
+vector<unique_ptr<S2Loop>> S2Polygon::Release() {
+ // Reset the polygon to be empty.
+ vector<unique_ptr<S2Loop>> loops;
+ loops.swap(loops_);
+ ClearLoops();
+ num_vertices_ = 0;
+ bound_ = S2LatLngRect::Empty();
+ subregion_bound_ = S2LatLngRect::Empty();
+ return loops;
+}
+
+void S2Polygon::ClearLoops() {
+ ClearIndex();
+ loops_.clear();
+ error_inconsistent_loop_orientations_ = false;
+}
+
+S2Polygon::~S2Polygon() {
+ ClearLoops();
+}
+
+bool S2Polygon::IsValid() const {
+ S2Error error;
+ if (FindValidationError(&error)) {
+ S2_LOG_IF(ERROR, FLAGS_s2debug) << error;
+ return false;
+ }
+ return true;
+}
+
+bool S2Polygon::FindValidationError(S2Error* error) const {
+ for (int i = 0; i < num_loops(); ++i) {
+ // Check for loop errors that don't require building an S2ShapeIndex.
+ if (loop(i)->FindValidationErrorNoIndex(error)) {
+ error->Init(error->code(),
+ "Loop %d: %s", i, error->text().c_str());
+ return true;
+ }
+ // Check that no loop is empty, and that the full loop only appears in the
+ // full polygon.
+ if (loop(i)->is_empty()) {
+ error->Init(S2Error::POLYGON_EMPTY_LOOP,
+ "Loop %d: empty loops are not allowed", i);
+ return true;
+ }
+ if (loop(i)->is_full() && num_loops() > 1) {
+ error->Init(S2Error::POLYGON_EXCESS_FULL_LOOP,
+ "Loop %d: full loop appears in non-full polygon", i);
+ return true;
+ }
+ }
+
+ // Check for loop self-intersections and loop pairs that cross
+ // (including duplicate edges and vertices).
+ if (s2shapeutil::FindSelfIntersection(index_, error)) return true;
+
+ // Check whether InitOriented detected inconsistent loop orientations.
+ if (error_inconsistent_loop_orientations_) {
+ error->Init(S2Error::POLYGON_INCONSISTENT_LOOP_ORIENTATIONS,
+ "Inconsistent loop orientations detected");
+ return true;
+ }
+
+ // Finally, verify the loop nesting hierarchy.
+ return FindLoopNestingError(error);
+}
+
+bool S2Polygon::FindLoopNestingError(S2Error* error) const {
+ // First check that the loop depths make sense.
+ for (int last_depth = -1, i = 0; i < num_loops(); ++i) {
+ int depth = loop(i)->depth();
+ if (depth < 0 || depth > last_depth + 1) {
+ error->Init(S2Error::POLYGON_INVALID_LOOP_DEPTH,
+ "Loop %d: invalid loop depth (%d)", i, depth);
+ return true;
+ }
+ last_depth = depth;
+ }
+ // Then check that they correspond to the actual loop nesting. This test
+ // is quadratic in the number of loops but the cost per iteration is small.
+ for (int i = 0; i < num_loops(); ++i) {
+ int last = GetLastDescendant(i);
+ for (int j = 0; j < num_loops(); ++j) {
+ if (i == j) continue;
+ bool nested = (j >= i + 1) && (j <= last);
+ const bool reverse_b = false;
+ if (loop(i)->ContainsNonCrossingBoundary(loop(j), reverse_b) != nested) {
+ error->Init(S2Error::POLYGON_INVALID_LOOP_NESTING,
+ "Invalid nesting: loop %d should %scontain loop %d",
+ i, nested ? "" : "not ", j);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void S2Polygon::InsertLoop(S2Loop* new_loop, S2Loop* parent,
+ LoopMap* loop_map) {
+ vector<S2Loop*>* children;
+ for (bool done = false; !done; ) {
+ children = &(*loop_map)[parent];
+ done = true;
+ for (S2Loop* child : *children) {
+ if (child->ContainsNested(new_loop)) {
+ parent = child;
+ done = false;
+ break;
+ }
+ }
+ }
+
+ // Some of the children of the parent loop may now be children of
+ // the new loop.
+ vector<S2Loop*>* new_children = &(*loop_map)[new_loop];
+ for (int i = 0; i < children->size();) {
+ S2Loop* child = (*children)[i];
+ if (new_loop->ContainsNested(child)) {
+ new_children->push_back(child);
+ children->erase(children->begin() + i);
+ } else {
+ ++i;
+ }
+ }
+ children->push_back(new_loop);
+}
+
+void S2Polygon::InitLoops(LoopMap* loop_map) {
+ std::stack<S2Loop*> loop_stack({nullptr});
+ int depth = -1;
+ while (!loop_stack.empty()) {
+ S2Loop* loop = loop_stack.top();
+ loop_stack.pop();
+ if (loop != nullptr) {
+ depth = loop->depth();
+ loops_.emplace_back(loop);
+ }
+ const vector<S2Loop*>& children = (*loop_map)[loop];
+ for (int i = children.size() - 1; i >= 0; --i) {
+ S2Loop* child = children[i];
+ S2_DCHECK(child != nullptr);
+ child->set_depth(depth + 1);
+ loop_stack.push(child);
+ }
+ }
+}
+
+void S2Polygon::InitIndex() {
+ S2_DCHECK_EQ(0, index_.num_shape_ids());
+ index_.Add(make_unique<Shape>(this));
+ if (!FLAGS_s2polygon_lazy_indexing) {
+ index_.ForceBuild();
+ }
+ if (FLAGS_s2debug && s2debug_override_ == S2Debug::ALLOW) {
+ // Note that FLAGS_s2debug is false in optimized builds (by default).
+ S2_CHECK(IsValid());
+ }
+}
+
+void S2Polygon::ClearIndex() {
+ unindexed_contains_calls_.store(0, std::memory_order_relaxed);
+ index_.Clear();
+}
+
+void S2Polygon::InitNested(vector<unique_ptr<S2Loop>> loops) {
+ ClearLoops();
+ loops_.swap(loops);
+
+ if (num_loops() == 1) {
+ InitOneLoop();
+ return;
+ }
+ LoopMap loop_map;
+ for (int i = 0; i < num_loops(); ++i) {
+ InsertLoop(loop(i), nullptr, &loop_map);
+ }
+ // Reorder the loops in depth-first traversal order.
+ // Loops are now owned by loop_map, don't let them be
+ // deleted by clear().
+ for (auto& loop : loops_) loop.release();
+ loops_.clear();
+ InitLoops(&loop_map);
+
+ // Compute num_vertices_, bound_, subregion_bound_.
+ InitLoopProperties();
+}
+
+void S2Polygon::Init(unique_ptr<S2Loop> loop) {
+ // We don't allow empty loops in the other Init() methods because deleting
+ // them changes the number of loops, which is awkward to handle.
+ ClearLoops();
+ if (loop->is_empty()) {
+ InitLoopProperties();
+ } else {
+ loops_.push_back(std::move(loop));
+ InitOneLoop();
+ }
+}
+
+// This is an internal method that expects that loops_ has already been
+// initialized with a single non-empty loop.
+void S2Polygon::InitOneLoop() {
+ S2_DCHECK_EQ(1, num_loops());
+ S2Loop* loop = loops_[0].get();
+ loop->set_depth(0);
+ error_inconsistent_loop_orientations_ = false;
+ num_vertices_ = loop->num_vertices();
+ bound_ = loop->GetRectBound();
+ subregion_bound_ = S2LatLngRectBounder::ExpandForSubregions(bound_);
+ InitIndex();
+}
+
+void S2Polygon::InitOriented(vector<unique_ptr<S2Loop>> loops) {
+ // Here is the algorithm:
+ //
+ // 1. Remember which of the given loops contain S2::Origin().
+ //
+ // 2. Invert loops as necessary to ensure that they are nestable (i.e., no
+ // loop contains the complement of any other loop). This may result in a
+ // set of loops corresponding to the complement of the given polygon, but
+ // we will fix that problem later.
+ //
+ // We make the loops nestable by first normalizing all the loops (i.e.,
+ // inverting any loops whose curvature is negative). This handles
+ // all loops except those whose curvature is very close to zero
+ // (within the maximum error tolerance). Any such loops are inverted if
+ // and only if they contain S2::Origin(). (In theory this step is only
+ // necessary if there are at least two such loops.) The resulting set of
+ // loops is guaranteed to be nestable.
+ //
+ // 3. Build the polygon. This yields either the desired polygon or its
+ // complement.
+ //
+ // 4. If there is at least one loop, we find a loop L that is adjacent to
+ // S2::Origin() (where "adjacent" means that there exists a path
+ // connecting S2::Origin() to some vertex of L such that the path does
+ // not cross any loop). There may be a single such adjacent loop, or
+ // there may be several (in which case they should all have the same
+ // contains_origin() value). We choose L to be the loop containing the
+ // origin whose depth is greatest, or loop(0) (a top-level shell) if no
+ // such loop exists.
+ //
+ // 5. If (L originally contained origin) != (polygon contains origin), we
+ // invert the polygon. This is done by inverting a top-level shell whose
+ // curvature is minimal and then fixing the nesting hierarchy. Note
+ // that because we normalized all the loops initially, this step is only
+ // necessary if the polygon requires at least one non-normalized loop to
+ // represent it.
+
+ set<const S2Loop*> contained_origin;
+ for (int i = 0; i < loops.size(); ++i) {
+ S2Loop* loop = loops[i].get();
+ if (loop->contains_origin()) {
+ contained_origin.insert(loop);
+ }
+ double angle = loop->GetCurvature();
+ if (fabs(angle) > loop->GetCurvatureMaxError()) {
+ // Normalize the loop.
+ if (angle < 0) loop->Invert();
+ } else {
+ // Ensure that the loop does not contain the origin.
+ if (loop->contains_origin()) loop->Invert();
+ }
+ }
+ InitNested(std::move(loops));
+ if (num_loops() > 0) {
+ S2Loop* origin_loop = loop(0);
+ bool polygon_contains_origin = false;
+ for (int i = 0; i < num_loops(); ++i) {
+ if (loop(i)->contains_origin()) {
+ polygon_contains_origin ^= true;
+ origin_loop = loop(i);
+ }
+ }
+ if (contained_origin.count(origin_loop) != polygon_contains_origin) {
+ Invert();
+ }
+ }
+ // Verify that the original loops had consistent shell/hole orientations.
+ // Each original loop L should have been inverted if and only if it now
+ // represents a hole.
+ for (int i = 0; i < loops_.size(); ++i) {
+ if ((contained_origin.count(loop(i)) != loop(i)->contains_origin()) !=
+ loop(i)->is_hole()) {
+ // There is no point in saving the loop index, because the error is a
+ // property of the entire set of loops. In general there is no way to
+ // determine which ones are incorrect.
+ error_inconsistent_loop_orientations_ = true;
+ if (FLAGS_s2debug && s2debug_override_ == S2Debug::ALLOW) {
+ // The FLAGS_s2debug validity checking usually happens in InitIndex(),
+ // but this error is detected too late for that.
+ S2_CHECK(IsValid()); // Always fails.
+ }
+ }
+ }
+}
+
+void S2Polygon::InitLoopProperties() {
+ num_vertices_ = 0;
+ bound_ = S2LatLngRect::Empty();
+ for (int i = 0; i < num_loops(); ++i) {
+ if (loop(i)->depth() == 0) {
+ bound_ = bound_.Union(loop(i)->GetRectBound());
+ }
+ num_vertices_ += loop(i)->num_vertices();
+ }
+ subregion_bound_ = S2LatLngRectBounder::ExpandForSubregions(bound_);
+ InitIndex();
+}
+
+int S2Polygon::GetParent(int k) const {
+ int depth = loop(k)->depth();
+ if (depth == 0) return -1; // Optimization.
+ while (--k >= 0 && loop(k)->depth() >= depth) continue;
+ return k;
+}
+
+int S2Polygon::GetLastDescendant(int k) const {
+ if (k < 0) return num_loops() - 1;
+ int depth = loop(k)->depth();
+ while (++k < num_loops() && loop(k)->depth() > depth) continue;
+ return k - 1;
+}
+
+double S2Polygon::GetArea() const {
+ double area = 0;
+ for (int i = 0; i < num_loops(); ++i) {
+ area += loop(i)->sign() * loop(i)->GetArea();
+ }
+ return area;
+}
+
+S2Point S2Polygon::GetCentroid() const {
+ S2Point centroid;
+ for (int i = 0; i < num_loops(); ++i) {
+ centroid += loop(i)->sign() * loop(i)->GetCentroid();
+ }
+ return centroid;
+}
+
+int S2Polygon::GetSnapLevel() const {
+ int snap_level = -1;
+ for (const unique_ptr<S2Loop>& child : loops_) {
+ for (int j = 0; j < child->num_vertices(); ++j) {
+ int face;
+ unsigned int si, ti;
+ int level = S2::XYZtoFaceSiTi(child->vertex(j), &face, &si, &ti);
+ if (level < 0) return level; // Vertex is not a cell center.
+ if (level != snap_level) {
+ if (snap_level < 0) {
+ snap_level = level; // First vertex.
+ } else {
+ return -1; // Vertices at more than one cell level.
+ }
+ }
+ }
+ }
+ return snap_level;
+}
+
+S1Angle S2Polygon::GetDistance(const S2Point& x) const {
+ // Note that S2Polygon::Contains(S2Point) is slightly more efficient than
+ // the generic version used by S2ClosestEdgeQuery.
+ if (Contains(x)) return S1Angle::Zero();
+ return GetDistanceToBoundary(x);
+}
+
+S1Angle S2Polygon::GetDistanceToBoundary(const S2Point& x) const {
+ S2ClosestEdgeQuery::Options options;
+ options.set_include_interiors(false);
+ S2ClosestEdgeQuery::PointTarget t(x);
+ return S2ClosestEdgeQuery(&index_, options).GetDistance(&t).ToAngle();
+}
+
+/*static*/ pair<double, double> S2Polygon::GetOverlapFractions(
+ const S2Polygon* a, const S2Polygon* b) {
+ S2Polygon intersection;
+ intersection.InitToIntersection(a, b);
+ double intersection_area = intersection.GetArea();
+ double a_area = a->GetArea();
+ double b_area = b->GetArea();
+ return std::make_pair(
+ intersection_area >= a_area ? 1 : intersection_area / a_area,
+ intersection_area >= b_area ? 1 : intersection_area / b_area);
+}
+
+S2Point S2Polygon::Project(const S2Point& x) const {
+ if (Contains(x)) return x;
+ return ProjectToBoundary(x);
+}
+
+S2Point S2Polygon::ProjectToBoundary(const S2Point& x) const {
+ S2ClosestEdgeQuery::Options options;
+ options.set_include_interiors(false);
+ S2ClosestEdgeQuery q(&index_, options);
+ S2ClosestEdgeQuery::PointTarget target(x);
+ S2ClosestEdgeQuery::Result edge = q.FindClosestEdge(&target);
+ return q.Project(x, edge);
+}
+
+bool S2Polygon::Contains(const S2Polygon* b) const {
+ // It's worth checking bounding rectangles, since they are precomputed.
+ // Note that the first bound has been expanded to account for possible
+ // numerical errors in the second bound.
+ if (!subregion_bound_.Contains(b->bound_)) {
+ // It is possible that A contains B even though Bound(A) does not contain
+ // Bound(B). This can only happen when polygon B has at least two outer
+ // shells and the union of the two bounds spans all longitudes. For
+ // example, suppose that B consists of two shells with a longitude gap
+ // between them, while A consists of one shell that surrounds both shells
+ // of B but goes the other way around the sphere (so that it does not
+ // intersect the longitude gap).
+ //
+ // For convenience we just check whether B has at least two loops rather
+ // than two outer shells.
+ if (b->num_loops() == 1 || !bound_.lng().Union(b->bound_.lng()).is_full()) {
+ return false;
+ }
+ }
+
+ // The following case is not handled by S2BooleanOperation because it only
+ // determines whether the boundary of the result is empty (which does not
+ // distinguish between the full and empty polygons).
+ if (is_empty() && b->is_full()) return false;
+
+ return S2BooleanOperation::Contains(index_, b->index_);
+}
+
+bool S2Polygon::Intersects(const S2Polygon* b) const {
+ // It's worth checking bounding rectangles, since they are precomputed.
+ if (!bound_.Intersects(b->bound_)) return false;
+
+ // The following case is not handled by S2BooleanOperation because it only
+ // determines whether the boundary of the result is empty (which does not
+ // distinguish between the full and empty polygons).
+ if (is_full() && b->is_full()) return true;
+
+ return S2BooleanOperation::Intersects(index_, b->index_);
+}
+
+S2Cap S2Polygon::GetCapBound() const {
+ return bound_.GetCapBound();
+}
+
+void S2Polygon::GetCellUnionBound(vector<S2CellId> *cell_ids) const {
+ return MakeS2ShapeIndexRegion(&index_).GetCellUnionBound(cell_ids);
+}
+
+bool S2Polygon::Contains(const S2Cell& target) const {
+ return MakeS2ShapeIndexRegion(&index_).Contains(target);
+}
+
+bool S2Polygon::ApproxContains(const S2Polygon* b, S1Angle tolerance) const {
+ S2Polygon difference;
+ difference.InitToApproxDifference(b, this, tolerance);
+ return difference.is_empty();
+}
+
+bool S2Polygon::ApproxDisjoint(const S2Polygon* b, S1Angle tolerance) const {
+ S2Polygon intersection;
+ intersection.InitToApproxIntersection(b, this, tolerance);
+ return intersection.is_empty();
+}
+
+bool S2Polygon::ApproxEquals(const S2Polygon* b, S1Angle tolerance) const {
+ // TODO(ericv): This can be implemented more cheaply with S2Builder, by
+ // simply adding all the edges from one polygon, adding the reversed edge
+ // from the other polygon, and turning on the options to split edges and
+ // discard sibling pairs. Then the polygons are approximately equal if the
+ // output graph has no edges.
+ S2Polygon symmetric_difference;
+ symmetric_difference.InitToApproxSymmetricDifference(b, this, tolerance);
+ return symmetric_difference.is_empty();
+}
+
+bool S2Polygon::MayIntersect(const S2Cell& target) const {
+ return MakeS2ShapeIndexRegion(&index_).MayIntersect(target);
+}
+
+bool S2Polygon::Contains(const S2Point& p) const {
+ // NOTE(ericv): A bounds check slows down this function by about 50%. It is
+ // worthwhile only when it might allow us to delay building the index.
+ if (!index_.is_fresh() && !bound_.Contains(p)) return false;
+
+ // For small polygons it is faster to just check all the crossings.
+ // Otherwise we keep track of the number of calls to Contains() and only
+ // build the index once enough calls have been made so that we think it is
+ // worth the effort. See S2Loop::Contains(S2Point) for detailed comments.
+ static const int kMaxBruteForceVertices = 32;
+ static const int kMaxUnindexedContainsCalls = 20;
+ if (num_vertices() <= kMaxBruteForceVertices ||
+ (!index_.is_fresh() &&
+ ++unindexed_contains_calls_ != kMaxUnindexedContainsCalls)) {
+ bool inside = false;
+ for (int i = 0; i < num_loops(); ++i) {
+ // Use brute force to avoid building the loop's S2ShapeIndex.
+ inside ^= loop(i)->BruteForceContains(p);
+ }
+ return inside;
+ }
+ // Otherwise we look up the S2ShapeIndex cell containing this point.
+ return MakeS2ContainsPointQuery(&index_).Contains(p);
+}
+
+void S2Polygon::Encode(Encoder* const encoder) const {
+ if (num_vertices_ == 0) {
+ EncodeCompressed(encoder, nullptr, S2::kMaxCellLevel);
+ return;
+ }
+ // Converts all the polygon vertices to S2XYZFaceSiTi format.
+ absl::FixedArray<S2XYZFaceSiTi> all_vertices(num_vertices_);
+ S2XYZFaceSiTi* current_loop_vertices = all_vertices.data();
+ for (const unique_ptr<S2Loop>& loop : loops_) {
+ loop->GetXYZFaceSiTiVertices(current_loop_vertices);
+ current_loop_vertices += loop->num_vertices();
+ }
+ // Computes a histogram of the cell levels at which the vertices are snapped.
+ // cell_level is -1 for unsnapped, or 0 through kMaxCellLevel if snapped,
+ // so we add one to it to get a non-negative index. (histogram[0] is the
+ // number of unsnapped vertices, histogram[i] the number of vertices
+ // snapped at level i-1).
+ std::array<int, S2::kMaxCellLevel + 2> histogram;
+ histogram.fill(0);
+ for (const auto& v : all_vertices) {
+ histogram[v.cell_level + 1] += 1;
+ }
+ // Compute the level at which most of the vertices are snapped.
+ // If multiple levels have the same maximum number of vertices
+ // snapped to it, the first one (lowest level number / largest
+ // area / smallest encoding length) will be chosen, so this
+ // is desired. Start with histogram[1] since histogram[0] is
+ // the number of unsnapped vertices, which we don't care about.
+ const auto max_iter =
+ std::max_element(histogram.begin() + 1, histogram.end());
+ // snap_level will be at position histogram[snap_level + 1], see above.
+ const int snap_level = max_iter - (histogram.begin() + 1);
+ const int num_snapped = *max_iter;
+ // Choose an encoding format based on the number of unsnapped vertices and a
+ // rough estimate of the encoded sizes.
+
+ // The compressed encoding requires approximately 4 bytes per vertex plus
+ // "exact_point_size" for each unsnapped vertex (encoded as an S2Point plus
+ // the index at which it is located).
+ int exact_point_size = sizeof(S2Point) + 2;
+ int num_unsnapped = num_vertices_ - num_snapped;
+ int compressed_size = 4 * num_vertices_ + exact_point_size * num_unsnapped;
+ int lossless_size = sizeof(S2Point) * num_vertices_;
+ if (compressed_size < lossless_size) {
+ EncodeCompressed(encoder, all_vertices.data(), snap_level);
+ } else {
+ EncodeUncompressed(encoder);
+ }
+}
+
+void S2Polygon::EncodeUncompressed(Encoder* const encoder) const {
+ encoder->Ensure(10); // Sufficient
+ encoder->put8(kCurrentUncompressedEncodingVersionNumber);
+ // This code used to write "owns_loops_", so write "true" for compatibility.
+ encoder->put8(true);
+ // Encode obsolete "has_holes_" field for backwards compatibility.
+ bool has_holes = false;
+ for (int i = 0; i < num_loops(); ++i) {
+ if (loop(i)->is_hole()) has_holes = true;
+ }
+ encoder->put8(has_holes);
+ encoder->put32(loops_.size());
+ S2_DCHECK_GE(encoder->avail(), 0);
+
+ for (int i = 0; i < num_loops(); ++i) {
+ loop(i)->Encode(encoder);
+ }
+ bound_.Encode(encoder);
+}
+
+bool S2Polygon::Decode(Decoder* const decoder) {
+ if (decoder->avail() < sizeof(unsigned char)) return false;
+ unsigned char version = decoder->get8();
+ switch (version) {
+ case kCurrentUncompressedEncodingVersionNumber:
+ return DecodeUncompressed(decoder, false);
+ case kCurrentCompressedEncodingVersionNumber:
+ return DecodeCompressed(decoder);
+ }
+ return false;
+}
+
+bool S2Polygon::DecodeWithinScope(Decoder* const decoder) {
+ if (decoder->avail() < sizeof(unsigned char)) return false;
+ unsigned char version = decoder->get8();
+ switch (version) {
+ case kCurrentUncompressedEncodingVersionNumber:
+ return DecodeUncompressed(decoder, true);
+ case kCurrentCompressedEncodingVersionNumber:
+ return DecodeCompressed(decoder);
+ }
+ return false;
+}
+
+bool S2Polygon::DecodeUncompressed(Decoder* const decoder, bool within_scope) {
+ if (decoder->avail() < 2 * sizeof(uint8) + sizeof(uint32)) return false;
+ ClearLoops();
+ decoder->get8(); // Ignore irrelevant serialized owns_loops_ value.
+ decoder->get8(); // Ignore irrelevant serialized has_holes_ value.
+ // Polygons with no loops are explicitly allowed here: a newly created
+ // polygon has zero loops and such polygons encode and decode properly.
+ const uint32 num_loops = decoder->get32();
+ if (num_loops > FLAGS_s2polygon_decode_max_num_loops) return false;
+ loops_.reserve(num_loops);
+ num_vertices_ = 0;
+ for (int i = 0; i < num_loops; ++i) {
+ loops_.push_back(make_unique<S2Loop>());
+ loops_.back()->set_s2debug_override(s2debug_override());
+ if (within_scope) {
+ if (!loops_.back()->DecodeWithinScope(decoder)) return false;
+ } else {
+ if (!loops_.back()->Decode(decoder)) return false;
+ }
+ num_vertices_ += loops_.back()->num_vertices();
+ }
+ if (!bound_.Decode(decoder)) return false;
+ subregion_bound_ = S2LatLngRectBounder::ExpandForSubregions(bound_);
+ InitIndex();
+ return true;
+}
+
+// TODO(ericv): Consider adding this to the S2Loop API. (May also want an
+// undirected version (CompareDirected vs CompareUndirected); should they
+// return a sign, or have separate "<" and "==" methods?)
+int S2Polygon::CompareLoops(const S2Loop* a, const S2Loop* b) {
+ if (a->num_vertices() != b->num_vertices()) {
+ return a->num_vertices() - b->num_vertices();
+ }
+ S2::LoopOrder ao = a->GetCanonicalLoopOrder();
+ S2::LoopOrder bo = b->GetCanonicalLoopOrder();
+ if (ao.dir != bo.dir) return ao.dir - bo.dir;
+ for (int n = a->num_vertices(), ai = ao.first, bi = bo.first;
+ --n >= 0; ai += ao.dir, bi += bo.dir) {
+ if (a->vertex(ai) < b->vertex(bi)) return -1;
+ if (a->vertex(ai) > b->vertex(bi)) return 1;
+ }
+ return 0;
+}
+
+void S2Polygon::Invert() {
+ // Inverting any one loop will invert the polygon. The best loop to invert
+ // is the one whose area is largest, since this yields the smallest area
+ // after inversion. The loop with the largest area is always at depth 0.
+ // The descendents of this loop all have their depth reduced by 1, while the
+ // former siblings of this loop all have their depth increased by 1.
+
+ // The empty and full polygons are handled specially.
+ if (is_empty()) {
+ loops_.push_back(make_unique<S2Loop>(S2Loop::kFull()));
+ } else if (is_full()) {
+ ClearLoops();
+ } else {
+ // Find the loop whose area is largest (i.e., whose curvature is
+ // smallest), minimizing calls to GetCurvature(). In particular, for
+ // polygons with a single shell at level 0 there is not need to call
+ // GetCurvature() at all. (This method is relatively expensive.)
+ int best = 0;
+ const double kNone = 10.0; // Flag that means "not computed yet"
+ double best_angle = kNone;
+ for (int i = 1; i < num_loops(); ++i) {
+ if (loop(i)->depth() == 0) {
+ // We defer computing the curvature of loop 0 until we discover
+ // that the polygon has another top-level shell.
+ if (best_angle == kNone) best_angle = loop(best)->GetCurvature();
+ double angle = loop(i)->GetCurvature();
+ // We break ties deterministically in order to avoid having the output
+ // depend on the input order of the loops.
+ if (angle < best_angle ||
+ (angle == best_angle && CompareLoops(loop(i), loop(best)) < 0)) {
+ best = i;
+ best_angle = angle;
+ }
+ }
+ }
+ // Build the new loops vector, starting with the inverted loop.
+ loop(best)->Invert();
+ vector<unique_ptr<S2Loop>> new_loops;
+ new_loops.reserve(num_loops());
+ // Add the former siblings of this loop as descendants.
+ int last_best = GetLastDescendant(best);
+ new_loops.push_back(std::move(loops_[best]));
+ for (int i = 0; i < num_loops(); ++i) {
+ if (i < best || i > last_best) {
+ loop(i)->set_depth(loop(i)->depth() + 1);
+ new_loops.push_back(std::move(loops_[i]));
+ }
+ }
+ // Add the former children of this loop as siblings.
+ for (int i = 0; i < num_loops(); ++i) {
+ if (i > best && i <= last_best) {
+ loop(i)->set_depth(loop(i)->depth() - 1);
+ new_loops.push_back(std::move(loops_[i]));
+ }
+ }
+ loops_.swap(new_loops);
+ S2_DCHECK_EQ(new_loops.size(), num_loops());
+ }
+ ClearIndex();
+ InitLoopProperties();
+}
+
+void S2Polygon::InitToComplement(const S2Polygon* a) {
+ Copy(a);
+ Invert();
+}
+
+bool S2Polygon::InitToOperation(S2BooleanOperation::OpType op_type,
+ const S2Builder::SnapFunction& snap_function,
+ const S2Polygon& a, const S2Polygon& b,
+ S2Error* error) {
+ S2BooleanOperation::Options options;
+ options.set_snap_function(snap_function);
+ S2BooleanOperation op(op_type, make_unique<S2PolygonLayer>(this),
+ options);
+ return op.Build(a.index_, b.index_, error);
+}
+
+void S2Polygon::InitToOperation(S2BooleanOperation::OpType op_type,
+ const S2Builder::SnapFunction& snap_function,
+ const S2Polygon& a, const S2Polygon& b) {
+ S2Error error;
+ if (!InitToOperation(op_type, snap_function, a, b, &error)) {
+ S2_LOG(DFATAL) << S2BooleanOperation::OpTypeToString(op_type)
+ << " operation failed: " << error;
+ }
+}
+
+void S2Polygon::InitToIntersection(const S2Polygon* a, const S2Polygon* b) {
+ InitToApproxIntersection(a, b, S2::kIntersectionMergeRadius);
+}
+
+void S2Polygon::InitToApproxIntersection(const S2Polygon* a, const S2Polygon* b,
+ S1Angle snap_radius) {
+ InitToIntersection(*a, *b, IdentitySnapFunction(snap_radius));
+}
+
+void S2Polygon::InitToIntersection(
+ const S2Polygon& a, const S2Polygon& b,
+ const S2Builder::SnapFunction& snap_function) {
+ if (!a.bound_.Intersects(b.bound_)) return;
+ InitToOperation(S2BooleanOperation::OpType::INTERSECTION,
+ snap_function, a, b);
+}
+
+bool S2Polygon::InitToIntersection(
+ const S2Polygon& a, const S2Polygon& b,
+ const S2Builder::SnapFunction& snap_function, S2Error* error) {
+ if (!a.bound_.Intersects(b.bound_)) return true; // Success.
+ return InitToOperation(S2BooleanOperation::OpType::INTERSECTION,
+ snap_function, a, b, error);
+}
+
+void S2Polygon::InitToUnion(const S2Polygon* a, const S2Polygon* b) {
+ InitToApproxUnion(a, b, S2::kIntersectionMergeRadius);
+}
+
+void S2Polygon::InitToApproxUnion(const S2Polygon* a, const S2Polygon* b,
+ S1Angle snap_radius) {
+ InitToUnion(*a, *b, IdentitySnapFunction(snap_radius));
+}
+
+void S2Polygon::InitToUnion(
+ const S2Polygon& a, const S2Polygon& b,
+ const S2Builder::SnapFunction& snap_function) {
+ InitToOperation(S2BooleanOperation::OpType::UNION, snap_function, a, b);
+}
+
+bool S2Polygon::InitToUnion(
+ const S2Polygon& a, const S2Polygon& b,
+ const S2Builder::SnapFunction& snap_function, S2Error* error) {
+ return InitToOperation(S2BooleanOperation::OpType::UNION,
+ snap_function, a, b, error);
+}
+
+void S2Polygon::InitToDifference(const S2Polygon* a, const S2Polygon* b) {
+ InitToApproxDifference(a, b, S2::kIntersectionMergeRadius);
+}
+
+void S2Polygon::InitToApproxDifference(const S2Polygon* a, const S2Polygon* b,
+ S1Angle snap_radius) {
+ InitToDifference(*a, *b, IdentitySnapFunction(snap_radius));
+}
+
+void S2Polygon::InitToDifference(
+ const S2Polygon& a, const S2Polygon& b,
+ const S2Builder::SnapFunction& snap_function) {
+ InitToOperation(S2BooleanOperation::OpType::DIFFERENCE, snap_function, a, b);
+}
+
+bool S2Polygon::InitToDifference(
+ const S2Polygon& a, const S2Polygon& b,
+ const S2Builder::SnapFunction& snap_function, S2Error* error) {
+ return InitToOperation(S2BooleanOperation::OpType::DIFFERENCE,
+ snap_function, a, b, error);
+}
+
+void S2Polygon::InitToSymmetricDifference(const S2Polygon* a,
+ const S2Polygon* b) {
+ InitToApproxSymmetricDifference(a, b, S2::kIntersectionMergeRadius);
+}
+
+void S2Polygon::InitToApproxSymmetricDifference(const S2Polygon* a,
+ const S2Polygon* b,
+ S1Angle snap_radius) {
+ InitToSymmetricDifference(*a, *b, IdentitySnapFunction(snap_radius));
+}
+
+void S2Polygon::InitToSymmetricDifference(
+ const S2Polygon& a, const S2Polygon& b,
+ const S2Builder::SnapFunction& snap_function) {
+ InitToOperation(S2BooleanOperation::OpType::SYMMETRIC_DIFFERENCE,
+ snap_function, a, b);
+}
+
+bool S2Polygon::InitToSymmetricDifference(
+ const S2Polygon& a, const S2Polygon& b,
+ const S2Builder::SnapFunction& snap_function, S2Error* error) {
+ return InitToOperation(S2BooleanOperation::OpType::SYMMETRIC_DIFFERENCE,
+ snap_function, a, b, error);
+}
+
+void S2Polygon::InitFromBuilder(const S2Polygon& a, S2Builder* builder) {
+ builder->StartLayer(make_unique<S2PolygonLayer>(this));
+ builder->AddPolygon(a);
+ S2Error error;
+ if (!builder->Build(&error)) {
+ S2_LOG(DFATAL) << "Could not build polygon: " << error;
+ }
+ // If there are no loops, check whether the result should be the full
+ // polygon rather than the empty one. (See InitToApproxIntersection.)
+ if (num_loops() == 0) {
+ if (a.bound_.Area() > 2 * M_PI && a.GetArea() > 2 * M_PI) Invert();
+ }
+}
+
+void S2Polygon::InitToSnapped(const S2Polygon* a, int snap_level) {
+ S2Builder builder{S2Builder::Options(S2CellIdSnapFunction(snap_level))};
+ InitFromBuilder(*a, &builder);
+}
+
+void S2Polygon::InitToSimplified(const S2Polygon& a,
+ const S2Builder::SnapFunction& snap_function) {
+ S2Builder::Options options(snap_function);
+ options.set_simplify_edge_chains(true);
+ S2Builder builder(options);
+ InitFromBuilder(a, &builder);
+}
+
+// Given a point "p" inside an S2Cell or on its boundary, return a mask
+// indicating which of the S2Cell edges the point lies on. All boundary
+// comparisons are to within a maximum "u" or "v" error of "tolerance_uv".
+// Bit "i" in the result is set if and only "p" is incident to the edge
+// corresponding to S2Cell::edge(i).
+uint8 GetCellEdgeIncidenceMask(const S2Cell& cell, const S2Point& p,
+ double tolerance_uv) {
+ uint8 mask = 0;
+ R2Point uv;
+ if (S2::FaceXYZtoUV(cell.face(), p, &uv)) {
+ R2Rect bound = cell.GetBoundUV();
+ if (FLAGS_s2debug) S2_DCHECK(bound.Expanded(tolerance_uv).Contains(uv));
+ if (fabs(uv[1] - bound[1][0]) <= tolerance_uv) mask |= 1;
+ if (fabs(uv[0] - bound[0][1]) <= tolerance_uv) mask |= 2;
+ if (fabs(uv[1] - bound[1][1]) <= tolerance_uv) mask |= 4;
+ if (fabs(uv[0] - bound[0][0]) <= tolerance_uv) mask |= 8;
+ }
+ return mask;
+}
+
+void S2Polygon::InitToSimplifiedInCell(
+ const S2Polygon* a, const S2Cell& cell,
+ S1Angle snap_radius, S1Angle boundary_tolerance) {
+ // The polygon to be simplified consists of "boundary edges" that follow the
+ // cell boundary and "interior edges" that do not. We want to simplify the
+ // interior edges while leaving the boundary edges unchanged. It's not
+ // sufficient to call S2Builder::ForceVertex() on all boundary vertices.
+ // For example, suppose the polygon includes a triangle ABC where all three
+ // vertices are on the cell boundary and B is a cell corner. Then if
+ // interior edge AC snaps to vertex B, this loop would become degenerate and
+ // be removed. Similarly, we don't want boundary edges to snap to interior
+ // vertices, since this also would cause portions of the polygon along the
+ // boundary to be removed.
+ //
+ // Instead we use a two-pass algorithm. In the first pass, we simplify
+ // *only* the interior edges, using ForceVertex() to ensure that any edge
+ // endpoints on the cell boundary do not move. In the second pass, we add
+ // the boundary edges (which are guaranteed to still form loops with the
+ // interior edges) and build the output polygon.
+ //
+ // Note that in theory, simplifying the interior edges could create an
+ // intersection with one of the boundary edges, since if two interior edges
+ // intersect very near the boundary then the intersection point could be
+ // slightly outside the cell (by at most S2::kIntersectionError).
+ // This is the *only* way that a self-intersection can be created, and it is
+ // expected to be extremely rare. Nevertheless we use a small snap radius
+ // in the second pass in order to eliminate any such self-intersections.
+ //
+ // We also want to preserve the cyclic vertex order of loops, so that the
+ // original polygon can be reconstructed when no simplification is possible
+ // (i.e., idempotency). In order to do this, we group the input edges into
+ // a sequence of polylines. Each polyline contains only one type of edge
+ // (interior or boundary). We use S2Builder to simplify the interior
+ // polylines, while the boundary polylines are passed through unchanged.
+ // Each interior polyline is in its own S2Builder layer in order to keep the
+ // edges in sequence. This lets us ensure that in the second pass, the
+ // edges are added in their original order so that S2PolygonLayer can
+ // reconstruct the original loops.
+
+ // We want an upper bound on how much "u" or "v" can change when a point on
+ // the boundary of the S2Cell is moved away by up to "boundary_tolerance".
+ // Inverting this, instead we could compute a lower bound on how far a point
+ // can move away from an S2Cell edge when "u" or "v" is changed by a given
+ // amount. The latter quantity is simply (S2::kMinWidth.deriv() / 2)
+ // under the S2_LINEAR_PROJECTION model, where we divide by 2 because we
+ // want the bound in terms of (u = 2 * s - 1) rather than "s" itself.
+ // Consulting s2metrics.cc, this value is sqrt(2/3)/2 = sqrt(1/6).
+ // Going back to the original problem, this gives:
+ double boundary_tolerance_uv = sqrt(6.0) * boundary_tolerance.radians();
+
+ // The first pass yields a collection of simplified polylines that preserve
+ // the original cyclic vertex order.
+ auto polylines = SimplifyEdgesInCell(*a, cell, boundary_tolerance_uv,
+ snap_radius);
+
+ // The second pass eliminates any intersections between interior edges and
+ // boundary edges, and then assembles the edges into a polygon.
+ S2Builder::Options options(
+ (IdentitySnapFunction(S2::kIntersectionError)));
+ options.set_idempotent(false); // Force snapping up to the given radius
+ S2Builder builder(options);
+ builder.StartLayer(make_unique<S2PolygonLayer>(this));
+ for (const auto& polyline : polylines) {
+ builder.AddPolyline(*polyline);
+ }
+ S2Error error;
+ if (!builder.Build(&error)) {
+ S2_LOG(DFATAL) << "Could not build polygon: " << error;
+ return;
+ }
+ // If there are no loops, check whether the result should be the full
+ // polygon rather than the empty one. (See InitToApproxIntersection.)
+ if (num_loops() == 0) {
+ if (a->bound_.Area() > 2 * M_PI && a->GetArea() > 2 * M_PI) Invert();
+ }
+}
+
+// See comments in InitToSimplifiedInCell.
+vector<unique_ptr<S2Polyline>> S2Polygon::SimplifyEdgesInCell(
+ const S2Polygon& a, const S2Cell& cell,
+ double tolerance_uv, S1Angle snap_radius) {
+ S2Builder::Options options((IdentitySnapFunction(snap_radius)));
+ options.set_simplify_edge_chains(true);
+ S2Builder builder(options);
+ // The output consists of a sequence of polylines. Polylines consisting of
+ // interior edges are simplified using S2Builder, while polylines consisting
+ // of boundary edges are returned unchanged.
+ vector<unique_ptr<S2Polyline>> polylines;
+ for (int i = 0; i < a.num_loops(); ++i) {
+ const S2Loop& a_loop = *a.loop(i);
+ const S2Point* v0 = &a_loop.oriented_vertex(0);
+ uint8 mask0 = GetCellEdgeIncidenceMask(cell, *v0, tolerance_uv);
+ bool in_interior = false; // Was the last edge an interior edge?
+ for (int j = 1; j <= a_loop.num_vertices(); ++j) {
+ const S2Point* v1 = &a_loop.oriented_vertex(j);
+ uint8 mask1 = GetCellEdgeIncidenceMask(cell, *v1, tolerance_uv);
+ if ((mask0 & mask1) != 0) {
+ // This is an edge along the cell boundary. Such edges do not get
+ // simplified; we add them directly to the output. (We create a
+ // separate polyline for each edge to keep things simple.) We call
+ // ForceVertex on all boundary vertices to ensure that they don't
+ // move, and so that nearby interior edges are snapped to them.
+ S2_DCHECK(!in_interior);
+ builder.ForceVertex(*v1);
+ polylines.emplace_back(new S2Polyline(vector<S2Point>{*v0, *v1}));
+ } else {
+ // This is an interior edge. If this is the first edge of an interior
+ // chain, then start a new S2Builder layer. Also ensure that any
+ // polyline vertices on the boundary do not move, so that they will
+ // still connect with any boundary edge(s) there.
+ if (!in_interior) {
+ S2Polyline* polyline = new S2Polyline;
+ builder.StartLayer(make_unique<S2PolylineLayer>(polyline));
+ polylines.emplace_back(polyline);
+ in_interior = true;
+ }
+ builder.AddEdge(*v0, *v1);
+ if (mask1 != 0) {
+ builder.ForceVertex(*v1);
+ in_interior = false; // Terminate this polyline.
+ }
+ }
+ v0 = v1;
+ mask0 = mask1;
+ }
+ }
+ S2Error error;
+ if (!builder.Build(&error)) {
+ S2_LOG(DFATAL) << "InitToSimplifiedInCell failed: " << error;
+ }
+ return polylines;
+}
+
+vector<unique_ptr<S2Polyline>> S2Polygon::OperationWithPolyline(
+ S2BooleanOperation::OpType op_type,
+ const S2Builder::SnapFunction& snap_function,
+ const S2Polyline& a) const {
+ S2BooleanOperation::Options options;
+ options.set_snap_function(snap_function);
+ vector<unique_ptr<S2Polyline>> result;
+ S2PolylineVectorLayer::Options layer_options;
+ layer_options.set_polyline_type(
+ S2PolylineVectorLayer::Options::PolylineType::WALK);
+ S2BooleanOperation op(
+ op_type, make_unique<S2PolylineVectorLayer>(&result, layer_options),
+ options);
+ MutableS2ShapeIndex a_index;
+ a_index.Add(make_unique<S2Polyline::Shape>(&a));
+ S2Error error;
+ if (!op.Build(a_index, index_, &error)) {
+ S2_LOG(DFATAL) << "Polyline " << S2BooleanOperation::OpTypeToString(op_type)
+ << " operation failed: " << error;
+ }
+ return result;
+}
+
+vector<unique_ptr<S2Polyline>> S2Polygon::IntersectWithPolyline(
+ const S2Polyline& a) const {
+ return ApproxIntersectWithPolyline(a, S2::kIntersectionMergeRadius);
+}
+
+vector<unique_ptr<S2Polyline>> S2Polygon::ApproxIntersectWithPolyline(
+ const S2Polyline& a, S1Angle snap_radius) const {
+ return IntersectWithPolyline(a, IdentitySnapFunction(snap_radius));
+}
+
+vector<unique_ptr<S2Polyline>> S2Polygon::IntersectWithPolyline(
+ const S2Polyline& a, const S2Builder::SnapFunction& snap_function) const {
+ return OperationWithPolyline(S2BooleanOperation::OpType::INTERSECTION,
+ snap_function, a);
+}
+
+vector<unique_ptr<S2Polyline>> S2Polygon::SubtractFromPolyline(
+ const S2Polyline& a) const {
+ return ApproxSubtractFromPolyline(a, S2::kIntersectionMergeRadius);
+}
+
+vector<unique_ptr<S2Polyline>> S2Polygon::ApproxSubtractFromPolyline(
+ const S2Polyline& a, S1Angle snap_radius) const {
+ return SubtractFromPolyline(a, IdentitySnapFunction(snap_radius));
+}
+
+vector<unique_ptr<S2Polyline>> S2Polygon::SubtractFromPolyline(
+ const S2Polyline& a, const S2Builder::SnapFunction& snap_function) const {
+ return OperationWithPolyline(S2BooleanOperation::OpType::DIFFERENCE,
+ snap_function, a);
+}
+
+bool S2Polygon::Contains(const S2Polyline& b) const {
+ return ApproxContains(b, S2::kIntersectionMergeRadius);
+}
+
+bool S2Polygon::ApproxContains(const S2Polyline& b, S1Angle tolerance) const {
+ auto difference = ApproxSubtractFromPolyline(b, tolerance);
+ return difference.empty();
+}
+
+bool S2Polygon::Intersects(const S2Polyline& b) const {
+ return !ApproxDisjoint(b, S2::kIntersectionMergeRadius);
+}
+
+bool S2Polygon::ApproxDisjoint(const S2Polyline& b, S1Angle tolerance) const {
+ auto intersection = ApproxIntersectWithPolyline(b, tolerance);
+ return intersection.empty();
+}
+
+unique_ptr<S2Polygon> S2Polygon::DestructiveUnion(
+ vector<unique_ptr<S2Polygon>> polygons) {
+ return DestructiveApproxUnion(std::move(polygons),
+ S2::kIntersectionMergeRadius);
+}
+
+unique_ptr<S2Polygon> S2Polygon::DestructiveApproxUnion(
+ vector<unique_ptr<S2Polygon>> polygons, S1Angle snap_radius) {
+ // Effectively create a priority queue of polygons in order of number of
+ // vertices. Repeatedly union the two smallest polygons and add the result
+ // to the queue until we have a single polygon to return.
+ using QueueType = std::multimap<int, unique_ptr<S2Polygon>>;
+ QueueType queue; // Map from # of vertices to polygon.
+ for (auto& polygon : polygons)
+ queue.insert(std::make_pair(polygon->num_vertices(), std::move(polygon)));
+
+ while (queue.size() > 1) {
+ // Pop two simplest polygons from queue.
+ QueueType::iterator smallest_it = queue.begin();
+ int a_size = smallest_it->first;
+ unique_ptr<S2Polygon> a_polygon(std::move(smallest_it->second));
+ queue.erase(smallest_it);
+ smallest_it = queue.begin();
+ int b_size = smallest_it->first;
+ unique_ptr<S2Polygon> b_polygon(std::move(smallest_it->second));
+ queue.erase(smallest_it);
+
+ // Union and add result back to queue.
+ auto union_polygon = make_unique<S2Polygon>();
+ union_polygon->InitToApproxUnion(a_polygon.get(), b_polygon.get(),
+ snap_radius);
+ queue.insert(std::make_pair(a_size + b_size, std::move(union_polygon)));
+ // We assume that the number of vertices in the union polygon is the
+ // sum of the number of vertices in the original polygons, which is not
+ // always true, but will almost always be a decent approximation, and
+ // faster than recomputing.
+ }
+
+ if (queue.empty())
+ return make_unique<S2Polygon>();
+ else
+ return std::move(queue.begin()->second);
+}
+
+void S2Polygon::InitToCellUnionBorder(const S2CellUnion& cells) {
+ // We use S2Builder to compute the union. Due to rounding errors, we can't
+ // compute an exact union - when a small cell is adjacent to a larger cell,
+ // the shared edges can fail to line up exactly. Two cell edges cannot come
+ // closer then kMinWidth, so if we have S2Builder snap edges within half
+ // that distance, then we should always merge shared edges without merging
+ // different edges.
+ double snap_radius = 0.5 * S2::kMinWidth.GetValue(S2CellId::kMaxLevel);
+ S2Builder builder{S2Builder::Options(
+ IdentitySnapFunction(S1Angle::Radians(snap_radius)))};
+ builder.StartLayer(make_unique<S2PolygonLayer>(this));
+ for (S2CellId id : cells) {
+ builder.AddLoop(S2Loop{S2Cell{id}});
+ }
+ S2Error error;
+ if (!builder.Build(&error)) {
+ S2_LOG(DFATAL) << "InitToCellUnionBorder failed: " << error;
+ }
+ // If there are no loops, check whether the result should be the full
+ // polygon rather than the empty one. There are only two ways that this can
+ // happen: either the cell union is empty, or it consists of all six faces.
+ if (num_loops() == 0) {
+ if (cells.empty()) return;
+ S2_DCHECK_EQ(uint64{6} << (2 * S2CellId::kMaxLevel),
+ cells.LeafCellsCovered());
+ Invert();
+ }
+}
+
+bool S2Polygon::IsNormalized() const {
+ // TODO(ericv): The condition tested here is insufficient. The correct
+ // condition is that each *connected component* of child loops can share at
+ // most one vertex with their parent loop. Example: suppose loop A has
+ // children B, C, D, and the following pairs are connected: AB, BC, CD, DA.
+ // Then the polygon is not normalized.
+ set<S2Point> vertices;
+ const S2Loop* last_parent = nullptr;
+ for (int i = 0; i < num_loops(); ++i) {
+ const S2Loop* child = loop(i);
+ if (child->depth() == 0) continue;
+ const S2Loop* parent = loop(GetParent(i));
+ if (parent != last_parent) {
+ vertices.clear();
+ for (int j = 0; j < parent->num_vertices(); ++j) {
+ vertices.insert(parent->vertex(j));
+ }
+ last_parent = parent;
+ }
+ int count = 0;
+ for (int j = 0; j < child->num_vertices(); ++j) {
+ if (vertices.count(child->vertex(j)) > 0) ++count;
+ }
+ if (count > 1) return false;
+ }
+ return true;
+}
+
+bool S2Polygon::Equals(const S2Polygon* b) const {
+ if (num_loops() != b->num_loops()) return false;
+ for (int i = 0; i < num_loops(); ++i) {
+ const S2Loop* a_loop = loop(i);
+ const S2Loop* b_loop = b->loop(i);
+ if ((b_loop->depth() != a_loop->depth()) || !b_loop->Equals(a_loop)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool S2Polygon::BoundaryEquals(const S2Polygon* b) const {
+ if (num_loops() != b->num_loops()) return false;
+
+ for (int i = 0; i < num_loops(); ++i) {
+ const S2Loop* a_loop = loop(i);
+ bool success = false;
+ for (int j = 0; j < num_loops(); ++j) {
+ const S2Loop* b_loop = b->loop(j);
+ if ((b_loop->depth() == a_loop->depth()) &&
+ b_loop->BoundaryEquals(a_loop)) {
+ success = true;
+ break;
+ }
+ }
+ if (!success) return false;
+ }
+ return true;
+}
+
+bool S2Polygon::BoundaryApproxEquals(const S2Polygon& b,
+ S1Angle max_error) const {
+ if (num_loops() != b.num_loops()) return false;
+
+ // For now, we assume that there is at most one candidate match for each
+ // loop. (So far this method is just used for testing.)
+
+ for (int i = 0; i < num_loops(); ++i) {
+ const S2Loop& a_loop = *loop(i);
+ bool success = false;
+ for (int j = 0; j < num_loops(); ++j) {
+ const S2Loop& b_loop = *b.loop(j);
+ if (b_loop.depth() == a_loop.depth() &&
+ b_loop.BoundaryApproxEquals(a_loop, max_error)) {
+ success = true;
+ break;
+ }
+ }
+ if (!success) return false;
+ }
+ return true;
+}
+
+bool S2Polygon::BoundaryNear(const S2Polygon& b, S1Angle max_error) const {
+ if (num_loops() != b.num_loops()) return false;
+
+ // For now, we assume that there is at most one candidate match for each
+ // loop. (So far this method is just used for testing.)
+
+ for (int i = 0; i < num_loops(); ++i) {
+ const S2Loop& a_loop = *loop(i);
+ bool success = false;
+ for (int j = 0; j < num_loops(); ++j) {
+ const S2Loop& b_loop = *b.loop(j);
+ if (b_loop.depth() == a_loop.depth() &&
+ b_loop.BoundaryNear(a_loop, max_error)) {
+ success = true;
+ break;
+ }
+ }
+ if (!success) return false;
+ }
+ return true;
+}
+
+void S2Polygon::EncodeCompressed(Encoder* encoder,
+ const S2XYZFaceSiTi* all_vertices,
+ int snap_level) const {
+ S2_CHECK_GE(snap_level, 0);
+ // Sufficient for what we write. Typically enough for a 4 vertex polygon.
+ encoder->Ensure(40);
+ encoder->put8(kCurrentCompressedEncodingVersionNumber);
+ encoder->put8(snap_level);
+ encoder->put_varint32(num_loops());
+ S2_DCHECK_GE(encoder->avail(), 0);
+ const S2XYZFaceSiTi* current_loop_vertices = all_vertices;
+ for (int i = 0; i < num_loops(); ++i) {
+ loops_[i]->EncodeCompressed(encoder, current_loop_vertices, snap_level);
+ current_loop_vertices += loops_[i]->num_vertices();
+ }
+ // Do not write the bound or num_vertices as they can be cheaply recomputed
+ // by DecodeCompressed. Microbenchmarks show the speed difference is
+ // inconsequential.
+}
+
+bool S2Polygon::DecodeCompressed(Decoder* decoder) {
+ if (decoder->avail() < sizeof(uint8)) return false;
+ ClearLoops();
+ int snap_level = decoder->get8();
+ if (snap_level > S2CellId::kMaxLevel) return false;
+ // Polygons with no loops are explicitly allowed here: a newly created
+ // polygon has zero loops and such polygons encode and decode properly.
+ uint32 num_loops;
+ if (!decoder->get_varint32(&num_loops)) return false;
+ if (num_loops > FLAGS_s2polygon_decode_max_num_loops) return false;
+ loops_.reserve(num_loops);
+ for (int i = 0; i < num_loops; ++i) {
+ auto loop = make_unique<S2Loop>();
+ loop->set_s2debug_override(s2debug_override());
+ if (!loop->DecodeCompressed(decoder, snap_level)) {
+ return false;
+ }
+ loops_.push_back(std::move(loop));
+ }
+ InitLoopProperties();
+ return true;
+}
+
+S2Polygon::Shape::Shape(const S2Polygon* polygon)
+ : cumulative_edges_(nullptr) {
+ Init(polygon);
+}
+
+void S2Polygon::Shape::Init(const S2Polygon* polygon) {
+ polygon_ = polygon;
+ delete[] cumulative_edges_;
+ cumulative_edges_ = nullptr;
+ num_edges_ = 0;
+ if (!polygon->is_full()) {
+ const int kMaxLinearSearchLoops = 12; // From benchmarks.
+ int num_loops = polygon->num_loops();
+ if (num_loops > kMaxLinearSearchLoops) {
+ cumulative_edges_ = new int[num_loops];
+ }
+ for (int i = 0; i < num_loops; ++i) {
+ if (cumulative_edges_) cumulative_edges_[i] = num_edges_;
+ num_edges_ += polygon->loop(i)->num_vertices();
+ }
+ }
+}
+
+S2Polygon::Shape::~Shape() {
+ delete[] cumulative_edges_;
+}
+
+S2Shape::Edge S2Polygon::Shape::edge(int e) const {
+ S2_DCHECK_LT(e, num_edges());
+ const S2Polygon* p = polygon();
+ int i;
+ if (cumulative_edges_) {
+ // "upper_bound" finds the loop just beyond the one we want.
+ int* start = std::upper_bound(cumulative_edges_,
+ cumulative_edges_ + p->num_loops(), e) - 1;
+ i = start - cumulative_edges_;
+ e -= *start;
+ } else {
+ // When the number of loops is small, linear search is faster. Most often
+ // there is exactly one loop and the code below executes zero times.
+ for (i = 0; e >= p->loop(i)->num_vertices(); ++i) {
+ e -= p->loop(i)->num_vertices();
+ }
+ }
+ return Edge(p->loop(i)->oriented_vertex(e),
+ p->loop(i)->oriented_vertex(e + 1));
+}
+
+S2Shape::ReferencePoint S2Polygon::Shape::GetReferencePoint() const {
+ const S2Polygon* p = polygon();
+ bool contains_origin = false;
+ for (int i = 0; i < p->num_loops(); ++i) {
+ contains_origin ^= p->loop(i)->contains_origin();
+ }
+ return ReferencePoint(S2::Origin(), contains_origin);
+}
+
+int S2Polygon::Shape::num_chains() const {
+ return polygon_->num_loops();
+}
+
+S2Shape::Chain S2Polygon::Shape::chain(int i) const {
+ S2_DCHECK_LT(i, Shape::num_chains());
+ if (cumulative_edges_) {
+ return Chain(cumulative_edges_[i], polygon_->loop(i)->num_vertices());
+ } else {
+ int e = 0;
+ for (int j = 0; j < i; ++j) e += polygon_->loop(j)->num_vertices();
+ // S2Polygon represents a full loop as a loop with one vertex, while
+ // S2Shape represents a full loop as a chain with no vertices.
+ int num_vertices = polygon_->loop(i)->num_vertices();
+ return Chain(e, (num_vertices == 1) ? 0 : num_vertices);
+ }
+}
+
+S2Shape::Edge S2Polygon::Shape::chain_edge(int i, int j) const {
+ S2_DCHECK_LT(i, Shape::num_chains());
+ S2_DCHECK_LT(j, polygon_->loop(i)->num_vertices());
+ return Edge(polygon()->loop(i)->oriented_vertex(j),
+ polygon()->loop(i)->oriented_vertex(j + 1));
+}
+
+S2Shape::ChainPosition S2Polygon::Shape::chain_position(int e) const {
+ // TODO(ericv): Make inline to remove code duplication with GetEdge.
+ S2_DCHECK_LT(e, num_edges());
+ const S2Polygon* p = polygon();
+ int i;
+ if (cumulative_edges_) {
+ // "upper_bound" finds the loop just beyond the one we want.
+ int* start = std::upper_bound(cumulative_edges_,
+ cumulative_edges_ + p->num_loops(), e) - 1;
+ i = start - cumulative_edges_;
+ e -= *start;
+ } else {
+ // When the number of loops is small, linear search is faster. Most often
+ // there is exactly one loop and the code below executes zero times.
+ for (i = 0; e >= p->loop(i)->num_vertices(); ++i) {
+ e -= p->loop(i)->num_vertices();
+ }
+ }
+ return ChainPosition(i, e);
+}
+
+size_t S2Polygon::SpaceUsed() const {
+ size_t size = sizeof(*this);
+ for (int i = 0; i < num_loops(); ++i) {
+ size += loop(i)->SpaceUsed();
+ }
+ size += index_.SpaceUsed() - sizeof(index_);
+ return size;
+}
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2polyline.h"
+
+#include <algorithm>
+#include <cmath>
+#include <functional>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "s2/base/commandlineflags.h"
+#include "s2/base/logging.h"
+#include "s2/third_party/absl/utility/utility.h"
+#include "s2/util/coding/coder.h"
+#include "s2/s1angle.h"
+#include "s2/s1interval.h"
+#include "s2/s2cap.h"
+#include "s2/s2cell.h"
+#include "s2/s2debug.h"
+#include "s2/s2edge_crosser.h"
+#include "s2/s2edge_distances.h"
+#include "s2/s2error.h"
+#include "s2/s2latlng_rect_bounder.h"
+#include "s2/s2pointutil.h"
+#include "s2/s2polyline_measures.h"
+#include "s2/s2predicates.h"
+#include "s2/util/math/matrix3x3.h"
+
+using std::max;
+using std::min;
+using std::set;
+using std::vector;
+
+static const unsigned char kCurrentLosslessEncodingVersionNumber = 1;
+
+S2Polyline::S2Polyline()
+ : s2debug_override_(S2Debug::ALLOW) {}
+
+S2Polyline::S2Polyline(S2Polyline&& other)
+ : s2debug_override_(other.s2debug_override_),
+ num_vertices_(absl::exchange(other.num_vertices_, 0)),
+ vertices_(std::move(other.vertices_)) {
+}
+
+S2Polyline& S2Polyline::operator=(S2Polyline&& other) {
+ s2debug_override_ = other.s2debug_override_;
+ num_vertices_ = absl::exchange(other.num_vertices_, 0);
+ vertices_ = std::move(other.vertices_);
+ return *this;
+}
+
+S2Polyline::S2Polyline(const vector<S2Point>& vertices)
+ : S2Polyline(vertices, S2Debug::ALLOW) {}
+
+S2Polyline::S2Polyline(const vector<S2LatLng>& vertices)
+ : S2Polyline(vertices, S2Debug::ALLOW) {}
+
+S2Polyline::S2Polyline(const vector<S2Point>& vertices,
+ S2Debug override)
+ : s2debug_override_(override) {
+ Init(vertices);
+}
+
+S2Polyline::S2Polyline(const vector<S2LatLng>& vertices,
+ S2Debug override)
+ : s2debug_override_(override) {
+ Init(vertices);
+}
+
+S2Polyline::~S2Polyline() {
+}
+
+void S2Polyline::set_s2debug_override(S2Debug override) {
+ s2debug_override_ = override;
+}
+
+S2Debug S2Polyline::s2debug_override() const {
+ return s2debug_override_;
+}
+
+void S2Polyline::Init(const vector<S2Point>& vertices) {
+ num_vertices_ = vertices.size();
+ vertices_.reset(new S2Point[num_vertices_]);
+ std::copy(vertices.begin(), vertices.end(), &vertices_[0]);
+ if (FLAGS_s2debug && s2debug_override_ == S2Debug::ALLOW) {
+ S2_CHECK(IsValid());
+ }
+}
+
+void S2Polyline::Init(const vector<S2LatLng>& vertices) {
+ num_vertices_ = vertices.size();
+ vertices_.reset(new S2Point[num_vertices_]);
+ for (int i = 0; i < num_vertices_; ++i) {
+ vertices_[i] = vertices[i].ToPoint();
+ }
+ if (FLAGS_s2debug && s2debug_override_ == S2Debug::ALLOW) {
+ S2_CHECK(IsValid());
+ }
+}
+
+bool S2Polyline::IsValid() const {
+ S2Error error;
+ if (FindValidationError(&error)) {
+ S2_LOG_IF(ERROR, FLAGS_s2debug) << error;
+ return false;
+ }
+ return true;
+}
+
+bool S2Polyline::FindValidationError(S2Error* error) const {
+ // All vertices must be unit length.
+ for (int i = 0; i < num_vertices(); ++i) {
+ if (!S2::IsUnitLength(vertex(i))) {
+ error->Init(S2Error::NOT_UNIT_LENGTH, "Vertex %d is not unit length", i);
+ return true;
+ }
+ }
+ // Adjacent vertices must not be identical or antipodal.
+ for (int i = 1; i < num_vertices(); ++i) {
+ if (vertex(i - 1) == vertex(i)) {
+ error->Init(S2Error::DUPLICATE_VERTICES,
+ "Vertices %d and %d are identical", i - 1, i);
+ return true;
+ }
+ if (vertex(i - 1) == -vertex(i)) {
+ error->Init(S2Error::ANTIPODAL_VERTICES,
+ "Vertices %d and %d are antipodal", i - 1, i);
+ return true;
+ }
+ }
+ return false;
+}
+
+S2Polyline::S2Polyline(const S2Polyline& src)
+ : num_vertices_(src.num_vertices_),
+ vertices_(new S2Point[num_vertices_]) {
+ std::copy(&src.vertices_[0], &src.vertices_[num_vertices_], &vertices_[0]);
+}
+
+S2Polyline* S2Polyline::Clone() const {
+ return new S2Polyline(*this);
+}
+
+S1Angle S2Polyline::GetLength() const {
+ return S2::GetLength(S2PointSpan(&vertices_[0], num_vertices_));
+}
+
+S2Point S2Polyline::GetCentroid() const {
+ return S2::GetCentroid(S2PointSpan(&vertices_[0], num_vertices_));
+}
+
+S2Point S2Polyline::GetSuffix(double fraction, int* next_vertex) const {
+ S2_DCHECK_GT(num_vertices(), 0);
+ // We intentionally let the (fraction >= 1) case fall through, since
+ // we need to handle it in the loop below in any case because of
+ // possible roundoff errors.
+ if (fraction <= 0) {
+ *next_vertex = 1;
+ return vertex(0);
+ }
+ S1Angle length_sum;
+ for (int i = 1; i < num_vertices(); ++i) {
+ length_sum += S1Angle(vertex(i-1), vertex(i));
+ }
+ S1Angle target = fraction * length_sum;
+ for (int i = 1; i < num_vertices(); ++i) {
+ S1Angle length(vertex(i-1), vertex(i));
+ if (target < length) {
+ // This interpolates with respect to arc length rather than
+ // straight-line distance, and produces a unit-length result.
+ S2Point result = S2::InterpolateAtDistance(target, vertex(i-1),
+ vertex(i));
+ // It is possible that (result == vertex(i)) due to rounding errors.
+ *next_vertex = (result == vertex(i)) ? (i + 1) : i;
+ return result;
+ }
+ target -= length;
+ }
+ *next_vertex = num_vertices();
+ return vertex(num_vertices() - 1);
+}
+
+S2Point S2Polyline::Interpolate(double fraction) const {
+ int next_vertex;
+ return GetSuffix(fraction, &next_vertex);
+}
+
+double S2Polyline::UnInterpolate(const S2Point& point, int next_vertex) const {
+ S2_DCHECK_GT(num_vertices(), 0);
+ if (num_vertices() < 2) {
+ return 0;
+ }
+ S1Angle length_sum;
+ for (int i = 1; i < next_vertex; ++i) {
+ length_sum += S1Angle(vertex(i-1), vertex(i));
+ }
+ S1Angle length_to_point = length_sum + S1Angle(vertex(next_vertex-1), point);
+ for (int i = next_vertex; i < num_vertices(); ++i) {
+ length_sum += S1Angle(vertex(i-1), vertex(i));
+ }
+ // The ratio can be greater than 1.0 due to rounding errors or because the
+ // point is not exactly on the polyline.
+ return min(1.0, length_to_point / length_sum);
+}
+
+S2Point S2Polyline::Project(const S2Point& point, int* next_vertex) const {
+ S2_DCHECK_GT(num_vertices(), 0);
+
+ if (num_vertices() == 1) {
+ // If there is only one vertex, it is always closest to any given point.
+ *next_vertex = 1;
+ return vertex(0);
+ }
+
+ // Initial value larger than any possible distance on the unit sphere.
+ S1Angle min_distance = S1Angle::Radians(10);
+ int min_index = -1;
+
+ // Find the line segment in the polyline that is closest to the point given.
+ for (int i = 1; i < num_vertices(); ++i) {
+ S1Angle distance_to_segment = S2::GetDistance(point, vertex(i-1),
+ vertex(i));
+ if (distance_to_segment < min_distance) {
+ min_distance = distance_to_segment;
+ min_index = i;
+ }
+ }
+ S2_DCHECK_NE(min_index, -1);
+
+ // Compute the point on the segment found that is closest to the point given.
+ S2Point closest_point =
+ S2::Project(point, vertex(min_index - 1), vertex(min_index));
+
+ *next_vertex = min_index + (closest_point == vertex(min_index) ? 1 : 0);
+ return closest_point;
+}
+
+bool S2Polyline::IsOnRight(const S2Point& point) const {
+ S2_DCHECK_GE(num_vertices(), 2);
+
+ int next_vertex;
+ S2Point closest_point = Project(point, &next_vertex);
+
+ S2_DCHECK_GE(next_vertex, 1);
+ S2_DCHECK_LE(next_vertex, num_vertices());
+
+ // If the closest point C is an interior vertex of the polyline, let B and D
+ // be the previous and next vertices. The given point P is on the right of
+ // the polyline (locally) if B, P, D are ordered CCW around vertex C.
+ if (closest_point == vertex(next_vertex-1) && next_vertex > 1 &&
+ next_vertex < num_vertices()) {
+ if (point == vertex(next_vertex-1))
+ return false; // Polyline vertices are not on the RHS.
+ return s2pred::OrderedCCW(vertex(next_vertex-2), point, vertex(next_vertex),
+ vertex(next_vertex-1));
+ }
+
+ // Otherwise, the closest point C is incident to exactly one polyline edge.
+ // We test the point P against that edge.
+ if (next_vertex == num_vertices())
+ --next_vertex;
+
+ return s2pred::Sign(point, vertex(next_vertex), vertex(next_vertex - 1)) > 0;
+}
+
+bool S2Polyline::Intersects(const S2Polyline* line) const {
+ if (num_vertices() <= 0 || line->num_vertices() <= 0) {
+ return false;
+ }
+
+ if (!GetRectBound().Intersects(line->GetRectBound())) {
+ return false;
+ }
+
+ // TODO(ericv): Use S2ShapeIndex here.
+ for (int i = 1; i < num_vertices(); ++i) {
+ S2EdgeCrosser crosser(
+ &vertex(i - 1), &vertex(i), &line->vertex(0));
+ for (int j = 1; j < line->num_vertices(); ++j) {
+ if (crosser.CrossingSign(&line->vertex(j)) >= 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void S2Polyline::Reverse() {
+ std::reverse(&vertices_[0], &vertices_[num_vertices_]);
+}
+
+S2LatLngRect S2Polyline::GetRectBound() const {
+ S2LatLngRectBounder bounder;
+ for (int i = 0; i < num_vertices(); ++i) {
+ bounder.AddPoint(vertex(i));
+ }
+ return bounder.GetBound();
+}
+
+S2Cap S2Polyline::GetCapBound() const {
+ return GetRectBound().GetCapBound();
+}
+
+bool S2Polyline::MayIntersect(const S2Cell& cell) const {
+ if (num_vertices() == 0) return false;
+
+ // We only need to check whether the cell contains vertex 0 for correctness,
+ // but these tests are cheap compared to edge crossings so we might as well
+ // check all the vertices.
+ for (int i = 0; i < num_vertices(); ++i) {
+ if (cell.Contains(vertex(i))) return true;
+ }
+ S2Point cell_vertices[4];
+ for (int i = 0; i < 4; ++i) {
+ cell_vertices[i] = cell.GetVertex(i);
+ }
+ for (int j = 0; j < 4; ++j) {
+ S2EdgeCrosser crosser(&cell_vertices[j], &cell_vertices[(j+1)&3],
+ &vertex(0));
+ for (int i = 1; i < num_vertices(); ++i) {
+ if (crosser.CrossingSign(&vertex(i)) >= 0) {
+ // There is a proper crossing, or two vertices were the same.
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void S2Polyline::Encode(Encoder* const encoder) const {
+ encoder->Ensure(num_vertices_ * sizeof(vertices_[0]) + 10); // sufficient
+
+ encoder->put8(kCurrentLosslessEncodingVersionNumber);
+ encoder->put32(num_vertices_);
+ encoder->putn(&vertices_[0], sizeof(vertices_[0]) * num_vertices_);
+
+ S2_DCHECK_GE(encoder->avail(), 0);
+}
+
+bool S2Polyline::Decode(Decoder* const decoder) {
+ if (decoder->avail() < sizeof(unsigned char) + sizeof(uint32)) return false;
+ unsigned char version = decoder->get8();
+ if (version > kCurrentLosslessEncodingVersionNumber) return false;
+
+ num_vertices_ = decoder->get32();
+ vertices_.reset(new S2Point[num_vertices_]);
+ if (decoder->avail() < num_vertices_ * sizeof(vertices_[0])) return false;
+ decoder->getn(&vertices_[0], num_vertices_ * sizeof(vertices_[0]));
+
+ if (FLAGS_s2debug && s2debug_override_ == S2Debug::ALLOW) {
+ S2_CHECK(IsValid());
+ }
+ return true;
+}
+
+namespace {
+
+// Given a polyline, a tolerance distance, and a start index, this function
+// returns the maximal end index such that the line segment between these two
+// vertices passes within "tolerance" of all interior vertices, in order.
+int FindEndVertex(const S2Polyline& polyline,
+ S1Angle tolerance, int index) {
+ S2_DCHECK_GE(tolerance.radians(), 0);
+ S2_DCHECK_LT((index + 1), polyline.num_vertices());
+
+ // The basic idea is to keep track of the "pie wedge" of angles from the
+ // starting vertex such that a ray from the starting vertex at that angle
+ // will pass through the discs of radius "tolerance" centered around all
+ // vertices processed so far.
+
+ // First we define a "coordinate frame" for the tangent and normal spaces
+ // at the starting vertex. Essentially this means picking three
+ // orthonormal vectors X,Y,Z such that X and Y span the tangent plane at
+ // the starting vertex, and Z is "up". We use the coordinate frame to
+ // define a mapping from 3D direction vectors to a one-dimensional "ray
+ // angle" in the range (-Pi, Pi]. The angle of a direction vector is
+ // computed by transforming it into the X,Y,Z basis, and then calculating
+ // atan2(y,x). This mapping allows us to represent a wedge of angles as a
+ // 1D interval. Since the interval wraps around, we represent it as an
+ // S1Interval, i.e. an interval on the unit circle.
+ Matrix3x3_d frame;
+ const S2Point& origin = polyline.vertex(index);
+ S2::GetFrame(origin, &frame);
+
+ // As we go along, we keep track of the current wedge of angles and the
+ // distance to the last vertex (which must be non-decreasing).
+ S1Interval current_wedge = S1Interval::Full();
+ double last_distance = 0;
+
+ for (++index; index < polyline.num_vertices(); ++index) {
+ const S2Point& candidate = polyline.vertex(index);
+ double distance = origin.Angle(candidate);
+
+ // We don't allow simplification to create edges longer than 90 degrees,
+ // to avoid numeric instability as lengths approach 180 degrees. (We do
+ // need to allow for original edges longer than 90 degrees, though.)
+ if (distance > M_PI/2 && last_distance > 0) break;
+
+ // Vertices must be in increasing order along the ray, except for the
+ // initial disc around the origin.
+ if (distance < last_distance && last_distance > tolerance.radians()) break;
+ last_distance = distance;
+
+ // Points that are within the tolerance distance of the origin do not
+ // constrain the ray direction, so we can ignore them.
+ if (distance <= tolerance.radians()) continue;
+
+ // If the current wedge of angles does not contain the angle to this
+ // vertex, then stop right now. Note that the wedge of possible ray
+ // angles is not necessarily empty yet, but we can't continue unless we
+ // are willing to backtrack to the last vertex that was contained within
+ // the wedge (since we don't create new vertices). This would be more
+ // complicated and also make the worst-case running time more than linear.
+ S2Point direction = S2::ToFrame(frame, candidate);
+ double center = atan2(direction.y(), direction.x());
+ if (!current_wedge.Contains(center)) break;
+
+ // To determine how this vertex constrains the possible ray angles,
+ // consider the triangle ABC where A is the origin, B is the candidate
+ // vertex, and C is one of the two tangent points between A and the
+ // spherical cap of radius "tolerance" centered at B. Then from the
+ // spherical law of sines, sin(a)/sin(A) = sin(c)/sin(C), where "a" and
+ // "c" are the lengths of the edges opposite A and C. In our case C is a
+ // 90 degree angle, therefore A = asin(sin(a) / sin(c)). Angle A is the
+ // half-angle of the allowable wedge.
+
+ double half_angle = asin(sin(tolerance.radians()) / sin(distance));
+ S1Interval target = S1Interval::FromPoint(center).Expanded(half_angle);
+ current_wedge = current_wedge.Intersection(target);
+ S2_DCHECK(!current_wedge.is_empty());
+ }
+ // We break out of the loop when we reach a vertex index that can't be
+ // included in the line segment, so back up by one vertex.
+ return index - 1;
+}
+} // namespace
+
+void S2Polyline::SubsampleVertices(S1Angle tolerance,
+ vector<int>* indices) const {
+ indices->clear();
+ if (num_vertices() == 0) return;
+
+ indices->push_back(0);
+ S1Angle clamped_tolerance = max(tolerance, S1Angle::Radians(0));
+ for (int index = 0; index + 1 < num_vertices(); ) {
+ int next_index = FindEndVertex(*this, clamped_tolerance, index);
+ // Don't create duplicate adjacent vertices.
+ if (vertex(next_index) != vertex(index)) {
+ indices->push_back(next_index);
+ }
+ index = next_index;
+ }
+}
+
+bool S2Polyline::Equals(const S2Polyline* b) const {
+ if (num_vertices() != b->num_vertices()) return false;
+ for (int offset = 0; offset < num_vertices(); ++offset) {
+ if (vertex(offset) != b->vertex(offset)) return false;
+ }
+ return true;
+}
+
+bool S2Polyline::ApproxEquals(const S2Polyline& b, S1Angle max_error) const {
+ if (num_vertices() != b.num_vertices()) return false;
+ for (int offset = 0; offset < num_vertices(); ++offset) {
+ if (!S2::ApproxEquals(vertex(offset), b.vertex(offset), max_error)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+size_t S2Polyline::SpaceUsed() const {
+ return sizeof(*this) + num_vertices() * sizeof(S2Point);
+}
+
+namespace {
+// Return the first i > "index" such that the ith vertex of "pline" is not at
+// the same point as the "index"th vertex. Returns pline.num_vertices() if
+// there is no such value of i.
+inline int NextDistinctVertex(const S2Polyline& pline, int index) {
+ const S2Point& initial = pline.vertex(index);
+ do {
+ ++index;
+ } while (index < pline.num_vertices() && pline.vertex(index) == initial);
+ return index;
+}
+
+// This struct represents a search state in the NearlyCovers algorithm
+// below. See the description of the algorithm for details.
+struct SearchState {
+ inline SearchState(int i_val, int j_val, bool i_in_progress_val)
+ : i(i_val), j(j_val), i_in_progress(i_in_progress_val) {}
+
+ int i;
+ int j;
+ bool i_in_progress;
+};
+
+// This operator is needed for storing SearchStates in a set. The ordering
+// chosen has no special meaning.
+struct SearchStateKeyCompare {
+ bool operator() (const SearchState& a, const SearchState& b) const {
+ if (a.i != b.i) return a.i < b.i;
+ if (a.j != b.j) return a.j < b.j;
+ return a.i_in_progress < b.i_in_progress;
+ }
+};
+
+} // namespace
+
+bool S2Polyline::NearlyCovers(const S2Polyline& covered,
+ S1Angle max_error) const {
+ // NOTE: This algorithm is described assuming that adjacent vertices in a
+ // polyline are never at the same point. That is, the ith and i+1th vertices
+ // of a polyline are never at the same point in space. The implementation
+ // does not make this assumption.
+
+ // DEFINITIONS:
+ // - edge "i" of a polyline is the edge from the ith to i+1th vertex.
+ // - covered_j is a polyline consisting of edges 0 through j of "covered."
+ // - this_i is a polyline consisting of edges 0 through i of this polyline.
+ //
+ // A search state is represented as an (int, int, bool) tuple, (i, j,
+ // i_in_progress). Using the "drive a car" analogy from the header comment, a
+ // search state signifies that you can drive one car along "covered" from its
+ // first vertex through a point on its jth edge, and another car along this
+ // polyline from some point on or before its ith edge to a to a point on its
+ // ith edge, such that no car ever goes backward, and the cars are always
+ // within "max_error" of each other. If i_in_progress is true, it means that
+ // you can definitely drive along "covered" through the jth vertex (beginning
+ // of the jth edge). Otherwise, you can definitely drive along "covered"
+ // through the point on the jth edge of "covered" closest to the ith vertex of
+ // this polyline.
+ //
+ // The algorithm begins by finding all edges of this polyline that are within
+ // "max_error" of the first vertex of "covered," and adding search states
+ // representing all of these possible starting states to the stack of
+ // "pending" states.
+ //
+ // The algorithm proceeds by popping the next pending state,
+ // (i,j,i_in_progress), off of the stack. First it checks to see if that
+ // state represents finding a valid covering of "covered" and returns true if
+ // so. Next, if the state represents reaching the end of this polyline
+ // without finding a successful covering, the algorithm moves on to the next
+ // state in the stack. Otherwise, if state (i+1,j,false) is valid, it is
+ // added to the stack of pending states. Same for state (i,j+1,true).
+ //
+ // We need the stack because when "i" and "j" can both be incremented,
+ // sometimes only one choice leads to a solution. We use a set to keep track
+ // of visited states to avoid duplicating work. With the set, the worst-case
+ // number of states examined is O(n+m) where n = this->num_vertices() and m =
+ // covered.num_vertices(). Without it, the amount of work could be as high as
+ // O((n*m)^2). Using set, the running time is O((n*m) log (n*m)).
+ //
+ // TODO(user): Benchmark this, and see if the set is worth it.
+
+ if (covered.num_vertices() == 0) return true;
+ if (this->num_vertices() == 0) return false;
+
+ vector<SearchState> pending;
+ set<SearchState, SearchStateKeyCompare> done;
+
+ // Find all possible starting states.
+ for (int i = 0, next_i = NextDistinctVertex(*this, 0), next_next_i;
+ next_i < this->num_vertices(); i = next_i, next_i = next_next_i) {
+ next_next_i = NextDistinctVertex(*this, next_i);
+ S2Point closest_point = S2::Project(
+ covered.vertex(0), this->vertex(i), this->vertex(next_i));
+
+ // In order to avoid duplicate starting states, we exclude the end vertex
+ // of each edge *except* for the last non-degenerate edge.
+ if ((next_next_i == this->num_vertices() ||
+ closest_point != this->vertex(next_i)) &&
+ S1Angle(closest_point, covered.vertex(0)) <= max_error) {
+ pending.push_back(SearchState(i, 0, true));
+ }
+ }
+
+ while (!pending.empty()) {
+ const SearchState state = pending.back();
+ pending.pop_back();
+ if (!done.insert(state).second) continue;
+
+ const int next_i = NextDistinctVertex(*this, state.i);
+ const int next_j = NextDistinctVertex(covered, state.j);
+ if (next_j == covered.num_vertices()) {
+ return true;
+ } else if (next_i == this->num_vertices()) {
+ continue;
+ }
+
+ S2Point i_begin, j_begin;
+ if (state.i_in_progress) {
+ j_begin = covered.vertex(state.j);
+ i_begin = S2::Project(
+ j_begin, this->vertex(state.i), this->vertex(next_i));
+ } else {
+ i_begin = this->vertex(state.i);
+ j_begin = S2::Project(
+ i_begin, covered.vertex(state.j), covered.vertex(next_j));
+ }
+
+ if (S2::IsEdgeBNearEdgeA(j_begin, covered.vertex(next_j),
+ i_begin, this->vertex(next_i), max_error)) {
+ pending.push_back(SearchState(next_i, state.j, false));
+ }
+ if (S2::IsEdgeBNearEdgeA(i_begin, this->vertex(next_i),
+ j_begin, covered.vertex(next_j), max_error)) {
+ pending.push_back(SearchState(state.i, next_j, true));
+ }
+ }
+ return false;
+}
+
+void S2Polyline::Shape::Init(const S2Polyline* polyline) {
+ S2_LOG_IF(WARNING, polyline->num_vertices() == 1)
+ << "S2Polyline::Shape with one vertex has no edges";
+ polyline_ = polyline;
+}
+
+int S2Polyline::Shape::num_chains() const {
+ return min(1, Shape::num_edges()); // Avoid virtual call.
+}
+
+S2Shape::Chain S2Polyline::Shape::chain(int i) const {
+ S2_DCHECK_EQ(i, 0);
+ return Chain(0, Shape::num_edges()); // Avoid virtual call.
+}
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+
+#include "s2/s2polyline_alignment.h"
+#include "s2/s2polyline_alignment_internal.h"
+
+#include <algorithm>
+#include <numeric>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "s2/base/logging.h"
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/util/math/mathutil.h"
+
+namespace s2polyline_alignment {
+
+Window::Window(const std::vector<ColumnStride>& strides) {
+ S2_DCHECK(!strides.empty()) << "Cannot construct empty window.";
+ S2_DCHECK(strides[0].start == 0) << "First element of start_cols is non-zero.";
+ strides_ = strides;
+ rows_ = strides.size();
+ cols_ = strides.back().end;
+ S2_DCHECK(this->IsValid()) << "Constructor validity check fail.";
+}
+
+Window::Window(const WarpPath& warp_path) {
+ S2_DCHECK(!warp_path.empty()) << "Cannot construct window from empty warp path.";
+ S2_DCHECK(warp_path.front() == std::make_pair(0, 0)) << "Must start at (0, 0).";
+ rows_ = warp_path.back().first + 1;
+ S2_DCHECK(rows_ > 0) << "Must have at least one row.";
+ cols_ = warp_path.back().second + 1;
+ S2_DCHECK(cols_ > 0) << "Must have at least one column.";
+ strides_.resize(rows_);
+
+ int prev_row = 0;
+ int curr_row = 0;
+ int stride_start = 0;
+ int stride_stop = 0;
+ for (const auto& pair : warp_path) {
+ curr_row = pair.first;
+ if (curr_row > prev_row) {
+ strides_[prev_row] = {stride_start, stride_stop};
+ stride_start = pair.second;
+ prev_row = curr_row;
+ }
+ stride_stop = pair.second + 1;
+ }
+ S2_DCHECK_EQ(curr_row, rows_ - 1);
+ strides_[rows_ - 1] = {stride_start, stride_stop};
+ S2_DCHECK(this->IsValid()) << "Constructor validity check fail.";
+}
+
+Window Window::Upsample(const int new_rows, const int new_cols) const {
+ S2_DCHECK(new_rows >= rows_) << "Upsampling: New_rows < current_rows";
+ S2_DCHECK(new_cols >= cols_) << "Upsampling: New_cols < current_cols";
+ const double row_scale = static_cast<double>(new_rows) / rows_;
+ const double col_scale = static_cast<double>(new_cols) / cols_;
+ std::vector<ColumnStride> new_strides(new_rows);
+ ColumnStride from_stride;
+ for (int row = 0; row < new_rows; ++row) {
+ from_stride = strides_[static_cast<int>((row + 0.5) / row_scale)];
+ new_strides[row] = {static_cast<int>(col_scale * from_stride.start + 0.5),
+ static_cast<int>(col_scale * from_stride.end + 0.5)};
+ }
+ return Window(new_strides);
+}
+
+// This code takes advantage of the fact that the dilation window is square to
+// ensure that we can compute the stride for each output row in constant time.
+// TODO (mrdmnd): a potential optimization might be to combine this method and
+// the Upsample method into a single "Expand" method. For the sake of
+// testing, I haven't done that here, but I think it would be fairly
+// straightforward to do so. This method generally isn't very expensive so it
+// feels unnecessary to combine them.
+Window Window::Dilate(const int radius) const {
+ S2_DCHECK(radius >= 0) << "Negative dilation radius.";
+ std::vector<ColumnStride> new_strides(rows_);
+ int prev_row, next_row;
+ for (int row = 0; row < rows_; ++row) {
+ prev_row = std::max(0, row - radius);
+ next_row = std::min(row + radius, rows_ - 1);
+ new_strides[row] = {std::max(0, strides_[prev_row].start - radius),
+ std::min(strides_[next_row].end + radius, cols_)};
+ }
+ return Window(new_strides);
+}
+
+// Debug string implemented primarily for testing purposes.
+string Window::DebugString() const {
+ std::stringstream buffer;
+ for (int row = 0; row < rows_; ++row) {
+ for (int col = 0; col < cols_; ++col) {
+ buffer << (strides_[row].InRange(col) ? " *" : " .");
+ }
+ buffer << std::endl;
+ }
+ return buffer.str();
+}
+
+// Valid Windows require the following structural conditions to hold:
+// 1) All rows must consist of a single contiguous stride of `true` values.
+// 2) All strides are greater than zero length (i.e. no empty rows).
+// 3) The index of the first `true` column in a row must be at least as
+// large as the index of the first `true` column in the previous row.
+// 4) The index of the last `true` column in a row must be at least as large
+// as the index of the last `true` column in the previous row.
+// 5) strides[0].start = 0 (the first cell is always filled).
+// 6) strides[n_rows-1].end = n_cols (the last cell is filled).
+bool Window::IsValid() const {
+ if (rows_ <= 0 || cols_ <= 0 || strides_.front().start != 0 ||
+ strides_.back().end != cols_) {
+ return false;
+ }
+
+ ColumnStride prev = {-1, -1};
+ for (const auto& curr : strides_) {
+ if (curr.end <= curr.start || curr.start < prev.start ||
+ curr.end < prev.end) {
+ return false;
+ }
+ prev = curr;
+ }
+ return true;
+}
+
+inline double BoundsCheckedTableCost(const int row, const int col,
+ const ColumnStride& stride,
+ const CostTable& table) {
+ if (row < 0 && col < 0) {
+ return 0.0;
+ } else if (row < 0 || col < 0 || !stride.InRange(col)) {
+ return DOUBLE_MAX;
+ } else {
+ return table[row][col];
+ }
+}
+
+// Perform dynamic timewarping by filling in the DP table on cells that are
+// inside our search window. For an exact (all-squares) evaluation, this
+// incurs bounds checking overhead - we don't need to ensure that we're inside
+// the appropriate cells in the window, because it's guaranteed. Structuring
+// the program to reuse code for both the EXACT and WINDOWED cases by
+// abstracting EXACT as a window with full-covering strides is done for
+// maintainability reasons. One potential optimization here might be to overload
+// this function to skip bounds checking when the window is full.
+//
+// As a note of general interest, the Dynamic Timewarp algorithm as stated here
+// prefers shorter warp paths, when two warp paths might be equally costly. This
+// is because it favors progressing in the sequences simultaneously due to the
+// equal weighting of a diagonal step in the cost table with a horizontal or
+// vertical step. This may be counterintuitive, but represents the standard
+// implementation of this algorithm. TODO(user) - future implementations could
+// allow weights on the lookup costs to mitigate this.
+//
+// This is the hottest routine in the whole package, please be careful to
+// profile any future changes made here.
+//
+// This method takes time proportional to the number of cells in the window,
+// which can range from O(max(a, b)) cells (best) to O(a*b) cells (worst)
+VertexAlignment DynamicTimewarp(const S2Polyline& a, const S2Polyline& b,
+ const Window& w) {
+ const int rows = a.num_vertices();
+ const int cols = b.num_vertices();
+ auto costs = CostTable(rows, std::vector<double>(cols));
+
+ ColumnStride curr;
+ ColumnStride prev = ColumnStride::All();
+ for (int row = 0; row < rows; ++row) {
+ curr = w.GetColumnStride(row);
+ for (int col = curr.start; col < curr.end; ++col) {
+ double d_cost = BoundsCheckedTableCost(row - 1, col - 1, prev, costs);
+ double u_cost = BoundsCheckedTableCost(row - 1, col - 0, prev, costs);
+ double l_cost = BoundsCheckedTableCost(row - 0, col - 1, curr, costs);
+ costs[row][col] = std::min({d_cost, u_cost, l_cost}) +
+ (a.vertex(row) - b.vertex(col)).Norm2();
+ }
+ prev = curr;
+ }
+
+ // Now we walk back through the cost table and build up the warp path.
+ // Somewhat surprisingly, it is faster to recover the path this way than it
+ // is to save the comparisons from the computation we *already did* to get the
+ // direction we came from. The author speculates that this behavior is
+ // assignment-cost-related: to persist direction, we have to do extra
+ // stores/loads of "directional" information, and the extra assignment cost
+ // this incurs is larger than the cost to simply redo the comparisons.
+ // It's probably worth revisiting this assumption in the future.
+ // As it turns out, the following code ends up effectively free.
+ WarpPath warp_path;
+ warp_path.reserve(std::max(a.num_vertices(), b.num_vertices()));
+ int row = a.num_vertices() - 1;
+ int col = b.num_vertices() - 1;
+ curr = w.GetCheckedColumnStride(row);
+ prev = w.GetCheckedColumnStride(row - 1);
+ while (row >= 0 && col >= 0) {
+ warp_path.push_back({row, col});
+ double d_cost = BoundsCheckedTableCost(row - 1, col - 1, prev, costs);
+ double u_cost = BoundsCheckedTableCost(row - 1, col - 0, prev, costs);
+ double l_cost = BoundsCheckedTableCost(row - 0, col - 1, curr, costs);
+ if (d_cost <= u_cost && d_cost <= l_cost) {
+ row -= 1;
+ col -= 1;
+ curr = w.GetCheckedColumnStride(row);
+ prev = w.GetCheckedColumnStride(row - 1);
+ } else if (u_cost <= l_cost) {
+ row -= 1;
+ curr = w.GetCheckedColumnStride(row);
+ prev = w.GetCheckedColumnStride(row - 1);
+ } else {
+ col -= 1;
+ }
+ }
+ std::reverse(warp_path.begin(), warp_path.end());
+ return VertexAlignment(costs.back().back(), warp_path);
+}
+
+std::unique_ptr<S2Polyline> HalfResolution(const S2Polyline& in) {
+ const int n = in.num_vertices();
+ std::vector<S2Point> vertices;
+ vertices.reserve(n / 2);
+ for (int i = 0; i < n; i += 2) {
+ vertices.push_back(in.vertex(i));
+ }
+ return absl::make_unique<S2Polyline>(vertices);
+}
+
+// Helper methods for GetMedoidPolyline and GetConsensusPolyline to auto-select
+// appropriate cost function / alignment functions.
+double CostFn(const S2Polyline& a, const S2Polyline& b, bool approx) {
+ return approx ? GetApproxVertexAlignment(a, b).alignment_cost
+ : GetExactVertexAlignmentCost(a, b);
+}
+
+VertexAlignment AlignmentFn(const S2Polyline& a, const S2Polyline& b,
+ bool approx) {
+ return approx ? GetApproxVertexAlignment(a, b)
+ : GetExactVertexAlignment(a, b);
+}
+
+// PUBLIC API IMPLEMENTATION DETAILS
+
+// This is the constant-space implementation of Dynamic Timewarp that can
+// compute the alignment cost, but not the warp path.
+double GetExactVertexAlignmentCost(const S2Polyline& a, const S2Polyline& b) {
+ const int a_n = a.num_vertices();
+ const int b_n = b.num_vertices();
+ S2_CHECK(a_n > 0) << "A is empty polyline.";
+ S2_CHECK(b_n > 0) << "B is empty polyline.";
+ std::vector<double> cost(b_n, DOUBLE_MAX);
+ double left_diag_min_cost = 0;
+ for (int row = 0; row < a_n; ++row) {
+ for (int col = 0; col < b_n; ++col) {
+ double up_cost = cost[col];
+ cost[col] = std::min(left_diag_min_cost, up_cost) +
+ (a.vertex(row) - b.vertex(col)).Norm2();
+ left_diag_min_cost = std::min(cost[col], up_cost);
+ }
+ left_diag_min_cost = DOUBLE_MAX;
+ }
+ return cost.back();
+}
+
+VertexAlignment GetExactVertexAlignment(const S2Polyline& a,
+ const S2Polyline& b) {
+ const int a_n = a.num_vertices();
+ const int b_n = b.num_vertices();
+ S2_CHECK(a_n > 0) << "A is empty polyline.";
+ S2_CHECK(b_n > 0) << "B is empty polyline.";
+ const auto w = Window(std::vector<ColumnStride>(a_n, {0, b_n}));
+ return DynamicTimewarp(a, b, w);
+}
+
+VertexAlignment GetApproxVertexAlignment(const S2Polyline& a,
+ const S2Polyline& b,
+ const int radius) {
+ // Determined experimentally, through benchmarking, as about the points at
+ // which ExactAlignment is faster than ApproxAlignment, so we use these as
+ // our switchover points to exact computation mode.
+ const int kSizeSwitchover = 32;
+ const double kDensitySwitchover = 0.85;
+ const int a_n = a.num_vertices();
+ const int b_n = b.num_vertices();
+ S2_CHECK(a_n > 0) << "A is empty polyline.";
+ S2_CHECK(b_n > 0) << "B is empty polyline.";
+ S2_CHECK(radius >= 0) << "Radius is negative.";
+
+ // If we've hit the point where doing a full, direct solve is guaranteed to
+ // be faster, then terminate the recursion and do that.
+ if (a_n - radius < kSizeSwitchover || b_n - radius < kSizeSwitchover) {
+ return GetExactVertexAlignment(a, b);
+ }
+
+ // If we've hit the point where the window will be probably be so full that we
+ // might as well compute an exact solution, then terminate recursion to do so.
+ if (std::max(a_n, b_n) * (2 * radius + 1) > a_n * b_n * kDensitySwitchover) {
+ return GetExactVertexAlignment(a, b);
+ }
+
+ // Otherwise, shrink the input polylines, recursively compute the vertex
+ // alignment using this method, and then compute the final alignment using
+ // the projected alignment `proj` on an upsampled, dilated window.
+ const auto a_half = HalfResolution(a);
+ const auto b_half = HalfResolution(b);
+ const auto proj = GetApproxVertexAlignment(*a_half, *b_half, radius);
+ const auto w = Window(proj.warp_path).Upsample(a_n, b_n).Dilate(radius);
+ return DynamicTimewarp(a, b, w);
+}
+
+// This method calls the approx method with a reasonable default for radius.
+VertexAlignment GetApproxVertexAlignment(const S2Polyline& a,
+ const S2Polyline& b) {
+ const int max_length = std::max(a.num_vertices(), b.num_vertices());
+ const int radius = static_cast<int>(std::pow(max_length, 0.25));
+ return GetApproxVertexAlignment(a, b, radius);
+}
+
+// We use some of the symmetry of our metric to avoid computing all N^2
+// alignments. Specifically, because cost_fn(a, b) = cost_fn(b, a), and
+// cost_fn(a, a) = 0, we can compute only the lower triangle of cost matrix
+// and then mirror it across the diagonal to save on cost_fn invocations.
+int GetMedoidPolyline(const std::vector<std::unique_ptr<S2Polyline>>& polylines,
+ const MedoidOptions options) {
+ const int num_polylines = polylines.size();
+ const bool approx = options.approx();
+ S2_CHECK_GT(num_polylines, 0);
+
+ // costs[i] stores total cost of aligning [i] with all other polylines.
+ std::vector<double> costs(num_polylines, 0.0);
+ for (int i = 0; i < num_polylines; ++i) {
+ for (int j = i + 1; j < num_polylines; ++j) {
+ double cost = CostFn(*polylines[i], *polylines[j], approx);
+ costs[i] += cost;
+ costs[j] += cost;
+ }
+ }
+ return std::min_element(costs.begin(), costs.end()) - costs.begin();
+}
+
+// Implements Iterative Dynamic Timewarp Barycenter Averaging algorithm from
+//
+// https://pdfs.semanticscholar.org/a596/8ca9488199291ffe5473643142862293d69d.pdf
+//
+// Algorithm:
+// Initialize consensus sequence with either the medoid or an arbitrary
+// element (chosen here to be the first element in the input collection).
+// While the consensus polyline `consensus` hasn't converged and we haven't
+// exceeded our iteration cap:
+// For each polyline `p` in the input,
+// Compute vertex alignment from the current consensus to `p`.
+// For each (c_index, p_index) pair in the warp path,
+// Add the S2Point pts->vertex(p_index) to S2Point consensus[c_index]
+// Normalize (compute centroid) of each consensus point.
+// Determine if consensus is converging; if no vertex has moved or we've hit
+// the iteration cap, halt.
+//
+// This algorithm takes O(iteration_cap * num_polylines) pairwise alignments.
+
+std::unique_ptr<S2Polyline> GetConsensusPolyline(
+ const std::vector<std::unique_ptr<S2Polyline>>& polylines,
+ const ConsensusOptions options) {
+ const int num_polylines = polylines.size();
+ S2_CHECK_GT(num_polylines, 0);
+ const bool approx = options.approx();
+
+ // Seed a consensus polyline, either arbitrarily with first element, or with
+ // the medoid. If seeding with medoid, inherit approx parameter from options.
+ int seed_index = 0;
+ if (options.seed_medoid()) {
+ MedoidOptions medoid_options;
+ medoid_options.set_approx(approx);
+ seed_index = GetMedoidPolyline(polylines, medoid_options);
+ }
+ auto consensus = std::unique_ptr<S2Polyline>(polylines[seed_index]->Clone());
+ const int num_consensus_vertices = consensus->num_vertices();
+ S2_DCHECK_GT(num_consensus_vertices, 1);
+
+ bool converged = false;
+ int iterations = 0;
+ while (!converged && iterations < options.iteration_cap()) {
+ std::vector<S2Point> points(num_consensus_vertices, S2Point());
+ for (const auto& polyline : polylines) {
+ const auto alignment = AlignmentFn(*consensus, *polyline, approx);
+ for (const auto& pair : alignment.warp_path) {
+ points[pair.first] += polyline->vertex(pair.second);
+ }
+ }
+ for (S2Point& p : points) {
+ p = p.Normalize();
+ }
+
+ ++iterations;
+ auto new_consensus = absl::make_unique<S2Polyline>(points);
+ converged = new_consensus->ApproxEquals(*consensus);
+ consensus = std::move(new_consensus);
+ }
+ return consensus;
+}
+} // namespace s2polyline_alignment
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2polyline_measures.h"
+
+#include <cmath>
+#include "s2/base/logging.h"
+#include "s2/s2centroids.h"
+
+namespace S2 {
+
+S1Angle GetLength(S2PointSpan polyline) {
+ S1Angle length;
+ for (int i = 1; i < polyline.size(); ++i) {
+ length += S1Angle(polyline[i - 1], polyline[i]);
+ }
+ return length;
+}
+
+S2Point GetCentroid(S2PointSpan polyline) {
+ S2Point centroid;
+ for (int i = 1; i < polyline.size(); ++i) {
+ centroid += S2::TrueCentroid(polyline[i - 1], polyline[i]);
+ }
+ return centroid;
+}
+
+} // namespace S2
--- /dev/null
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2polyline_simplifier.h"
+
+#include <cfloat>
+
+#include "s2/s1chord_angle.h"
+#include "s2/s1interval.h"
+
+void S2PolylineSimplifier::Init(const S2Point& src) {
+ src_ = src;
+ window_ = S1Interval::Full();
+
+ // Precompute basis vectors for the tangent space at "src". This is similar
+ // to GetFrame() except that we don't normalize the vectors. As it turns
+ // out, the two basis vectors below have the same magnitude (up to the
+ // length error in S2Point::Normalize).
+
+ // Find the index of the component whose magnitude is smallest.
+ S2Point tmp = src.Abs();
+ int i = (tmp[0] < tmp[1] ?
+ (tmp[0] < tmp[2] ? 0 : 2) : (tmp[1] < tmp[2] ? 1 : 2));
+
+ // We define the "y" basis vector as the cross product of "src" and the
+ // basis vector for axis "i". Let "j" and "k" be the indices of the other
+ // two components in cyclic order.
+ int j = (i == 2 ? 0 : i + 1), k = (i == 0 ? 2 : i - 1);
+ y_dir_[i] = 0;
+ y_dir_[j] = src[k];
+ y_dir_[k] = -src[j];
+
+ // Compute the cross product of "y_dir" and "src". We write out the cross
+ // product here mainly for documentation purposes; it also happens to save a
+ // few multiplies because unfortunately the optimizer does *not* get rid of
+ // multiplies by zero (since these multiplies propagate NaN, for example).
+ x_dir_[i] = src[j] * src[j] + src[k] * src[k];
+ x_dir_[j] = -src[j] * src[i];
+ x_dir_[k] = -src[k] * src[i];
+}
+
+bool S2PolylineSimplifier::Extend(const S2Point& dst) const {
+ // We limit the maximum edge length to 90 degrees in order to simplify the
+ // error bounds. (The error gets arbitrarily large as the edge length
+ // approaches 180 degrees.)
+ if (S1ChordAngle(src_, dst) > S1ChordAngle::Right()) return false;
+
+ // Otherwise check whether this vertex is in the acceptable angle range.
+ return window_.Contains(GetAngle(dst));
+}
+
+bool S2PolylineSimplifier::TargetDisc(const S2Point& p, S1ChordAngle r) {
+ // Shrink the target interval by the maximum error from all sources. This
+ // guarantees that the output edge will intersect the given disc.
+ double semiwidth = GetSemiwidth(p, r, -1 /*round down*/);
+ if (semiwidth >= M_PI) {
+ // The target disc contains "src", so there is nothing to do.
+ return true;
+ }
+ if (semiwidth < 0) {
+ window_ = S1Interval::Empty();
+ return false;
+ }
+ // Otherwise compute the angle interval corresponding to the target disc and
+ // intersect it with the current window.
+ double center = GetAngle(p);
+ S1Interval target = S1Interval::FromPoint(center).Expanded(semiwidth);
+ window_ = window_.Intersection(target);
+ return !window_.is_empty();
+}
+
+bool S2PolylineSimplifier::AvoidDisc(const S2Point& p, S1ChordAngle r,
+ bool disc_on_left) {
+ // Expand the interval by the maximum error from all sources. This
+ // guarantees that the final output edge will avoid the given disc.
+ double semiwidth = GetSemiwidth(p, r, 1 /*round up*/);
+ if (semiwidth >= M_PI) {
+ // The avoidance disc contains "src", so it is impossible to avoid.
+ window_ = S1Interval::Empty();
+ return false;
+ }
+ double center = GetAngle(p);
+ double opposite = (center > 0) ? center - M_PI : center + M_PI;
+ S1Interval target = (disc_on_left ? S1Interval(opposite, center) :
+ S1Interval(center, opposite));
+ window_ = window_.Intersection(target.Expanded(-semiwidth));
+ return !window_.is_empty();
+}
+
+double S2PolylineSimplifier::GetAngle(const S2Point& p) const {
+ return atan2(p.DotProd(y_dir_), p.DotProd(x_dir_));
+}
+
+double S2PolylineSimplifier::GetSemiwidth(const S2Point& p, S1ChordAngle r,
+ int round_direction) const {
+ double constexpr DBL_ERR = 0.5 * DBL_EPSILON;
+
+ // Using spherical trigonometry,
+ //
+ // sin(semiwidth) = sin(r) / sin(a)
+ //
+ // where "a" is the angle between "src" and "p". Rather than measuring
+ // these angles, instead we measure the squared chord lengths through the
+ // interior of the sphere (i.e., Cartersian distance). Letting "r2" be the
+ // squared chord distance corresponding to "r", and "a2" be the squared
+ // chord distance corresponding to "a", we use the relationships
+ //
+ // sin^2(r) = r2 (1 - r2 / 4)
+ // sin^2(a) = d2 (1 - d2 / 4)
+ //
+ // which follow from the fact that r2 = (2 * sin(r / 2)) ^ 2, etc.
+
+ // "a2" has a relative error up to 5 * DBL_ERR, plus an absolute error of up
+ // to 64 * DBL_ERR^2 (because "src" and "p" may differ from unit length by
+ // up to 4 * DBL_ERR). We can correct for the relative error later, but for
+ // the absolute error we use "round_direction" to account for it now.
+ double r2 = r.length2();
+ double a2 = S1ChordAngle(src_, p).length2();
+ a2 -= 64 * DBL_ERR * DBL_ERR * round_direction;
+ if (a2 <= r2) return M_PI; // The given disc contains "src".
+
+ double sin2_r = r2 * (1 - 0.25 * r2);
+ double sin2_a = a2 * (1 - 0.25 * a2);
+ double semiwidth = asin(sqrt(sin2_r / sin2_a));
+
+ // We compute bounds on the errors from all sources:
+ //
+ // - The call to GetSemiwidth (this call).
+ // - The call to GetAngle that computes the center of the interval.
+ // - The call to GetAngle in Extend that tests whether a given point
+ // is an acceptable destination vertex.
+ //
+ // Summary of the errors in GetAngle:
+ //
+ // y_dir_ has no error.
+ //
+ // x_dir_ has a relative error of DBL_ERR in two components, a relative
+ // error of 2 * DBL_ERR in the other component, plus an overall relative
+ // length error of 4 * DBL_ERR (compared to y_dir_) because "src" is assumed
+ // to be normalized only to within the tolerances of S2Point::Normalize().
+ //
+ // p.DotProd(y_dir_) has a relative error of 1.5 * DBL_ERR and an
+ // absolute error of 1.5 * DBL_ERR * y_dir_.Norm().
+ //
+ // p.DotProd(x_dir_) has a relative error of 5.5 * DBL_ERR and an absolute
+ // error of 3.5 * DBL_ERR * y_dir_.Norm() (noting that x_dir_ and y_dir_
+ // have the same length to within a relative error of 4 * DBL_ERR).
+ //
+ // It's possible to show by taking derivatives that these errors can affect
+ // the angle atan2(y, x) by up 7.093 * DBL_ERR radians. Rounding up and
+ // including the call to atan2 gives a final error bound of 10 * DBL_ERR.
+ //
+ // Summary of the errors in GetSemiwidth:
+ //
+ // The distance a2 has a relative error of 5 * DBL_ERR plus an absolute
+ // error of 64 * DBL_ERR^2 because the points "src" and "p" may differ from
+ // unit length (by up to 4 * DBL_ERR). We have already accounted for the
+ // absolute error above, leaving only the relative error.
+ //
+ // sin2_r has a relative error of 2 * DBL_ERR.
+ //
+ // sin2_a has a relative error of 12 * DBL_ERR assuming that a2 <= 2,
+ // i.e. distance(src, p) <= 90 degrees. (The relative error gets
+ // arbitrarily larger as this distance approaches 180 degrees.)
+ //
+ // semiwidth has a relative error of 17 * DBL_ERR.
+ //
+ // Finally, (center +/- semiwidth) has a rounding error of up to 4 * DBL_ERR
+ // because in theory, the result magnitude may be as large as 1.5 * M_PI
+ // which is larger than 4.0. This gives a total error of:
+ double error = (2 * 10 + 4) * DBL_ERR + 17 * DBL_ERR * semiwidth;
+ return semiwidth + round_direction * error;
+}
--- /dev/null
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2predicates.h"
+#include "s2/s2predicates_internal.h"
+
+#include <algorithm>
+#include <cfloat>
+#include <cmath>
+#include <ostream>
+#include "s2/s1chord_angle.h"
+#include "s2/util/math/exactfloat/exactfloat.h"
+#include "s2/util/math/vector.h"
+
+using std::fabs;
+using std::max;
+using std::min;
+using std::sqrt;
+
+namespace s2pred {
+
+// All error bounds in this file are expressed in terms of the maximum
+// rounding error for a floating-point type. The rounding error is half of
+// the numeric_limits<T>::epsilon() value.
+constexpr double DBL_ERR = rounding_epsilon<double>();
+constexpr long double LD_ERR = rounding_epsilon<long double>();
+
+// A predefined S1ChordAngle representing (approximately) 45 degrees.
+static const S1ChordAngle k45Degrees = S1ChordAngle::FromLength2(2 - M_SQRT2);
+
+int Sign(const S2Point& a, const S2Point& b, const S2Point& c) {
+ // We don't need RobustCrossProd() here because Sign() does its own
+ // error estimation and calls ExpensiveSign() if there is any uncertainty
+ // about the result.
+ return Sign(a, b, c, a.CrossProd(b));
+}
+
+// Compute the determinant in a numerically stable way. Unlike TriageSign(),
+// this method can usually compute the correct determinant sign even when all
+// three points are as collinear as possible. For example if three points are
+// spaced 1km apart along a random line on the Earth's surface using the
+// nearest representable points, there is only a 0.4% chance that this method
+// will not be able to find the determinant sign. The probability of failure
+// decreases as the points get closer together; if the collinear points are
+// 1 meter apart, the failure rate drops to 0.0004%.
+//
+// This method could be extended to also handle nearly-antipodal points (and
+// in fact an earlier version of this code did exactly that), but antipodal
+// points are rare in practice so it seems better to simply fall back to
+// exact arithmetic in that case.
+int StableSign(const S2Point& a, const S2Point& b, const S2Point& c) {
+ Vector3_d ab = b - a;
+ Vector3_d bc = c - b;
+ Vector3_d ca = a - c;
+ double ab2 = ab.Norm2();
+ double bc2 = bc.Norm2();
+ double ca2 = ca.Norm2();
+
+ // Now compute the determinant ((A-C)x(B-C)).C, where the vertices have been
+ // cyclically permuted if necessary so that AB is the longest edge. (This
+ // minimizes the magnitude of cross product.) At the same time we also
+ // compute the maximum error in the determinant. Using a similar technique
+ // to the one used for kMaxDetError, the error is at most
+ //
+ // |d| <= (3 + 6/sqrt(3)) * |A-C| * |B-C| * e
+ //
+ // where e = 0.5 * DBL_EPSILON. If the determinant magnitude is larger than
+ // this value then we know its sign with certainty.
+ const double kDetErrorMultiplier = 3.2321 * DBL_EPSILON; // see above
+ double det, max_error;
+ if (ab2 >= bc2 && ab2 >= ca2) {
+ // AB is the longest edge, so compute (A-C)x(B-C).C.
+ det = -(ca.CrossProd(bc).DotProd(c));
+ max_error = kDetErrorMultiplier * sqrt(ca2 * bc2);
+ } else if (bc2 >= ca2) {
+ // BC is the longest edge, so compute (B-A)x(C-A).A.
+ det = -(ab.CrossProd(ca).DotProd(a));
+ max_error = kDetErrorMultiplier * sqrt(ab2 * ca2);
+ } else {
+ // CA is the longest edge, so compute (C-B)x(A-B).B.
+ det = -(bc.CrossProd(ab).DotProd(b));
+ max_error = kDetErrorMultiplier * sqrt(bc2 * ab2);
+ }
+ return (fabs(det) <= max_error) ? 0 : (det > 0) ? 1 : -1;
+}
+
+// The following function returns the sign of the determinant of three points
+// A, B, C under a model where every possible S2Point is slightly perturbed by
+// a unique infinitesmal amount such that no three perturbed points are
+// collinear and no four points are coplanar. The perturbations are so small
+// that they do not change the sign of any determinant that was non-zero
+// before the perturbations, and therefore can be safely ignored unless the
+// determinant of three points is exactly zero (using multiple-precision
+// arithmetic).
+//
+// Since the symbolic perturbation of a given point is fixed (i.e., the
+// perturbation is the same for all calls to this method and does not depend
+// on the other two arguments), the results of this method are always
+// self-consistent. It will never return results that would correspond to an
+// "impossible" configuration of non-degenerate points.
+//
+// Requirements:
+// The 3x3 determinant of A, B, C must be exactly zero.
+// The points must be distinct, with A < B < C in lexicographic order.
+//
+// Returns:
+// +1 or -1 according to the sign of the determinant after the symbolic
+// perturbations are taken into account.
+//
+// Reference:
+// "Simulation of Simplicity" (Edelsbrunner and Muecke, ACM Transactions on
+// Graphics, 1990).
+//
+int SymbolicallyPerturbedSign(
+ const Vector3_xf& a, const Vector3_xf& b,
+ const Vector3_xf& c, const Vector3_xf& b_cross_c) {
+ // This method requires that the points are sorted in lexicographically
+ // increasing order. This is because every possible S2Point has its own
+ // symbolic perturbation such that if A < B then the symbolic perturbation
+ // for A is much larger than the perturbation for B.
+ //
+ // Alternatively, we could sort the points in this method and keep track of
+ // the sign of the permutation, but it is more efficient to do this before
+ // converting the inputs to the multi-precision representation, and this
+ // also lets us re-use the result of the cross product B x C.
+ S2_DCHECK(a < b && b < c);
+
+ // Every input coordinate x[i] is assigned a symbolic perturbation dx[i].
+ // We then compute the sign of the determinant of the perturbed points,
+ // i.e.
+ // | a[0]+da[0] a[1]+da[1] a[2]+da[2] |
+ // | b[0]+db[0] b[1]+db[1] b[2]+db[2] |
+ // | c[0]+dc[0] c[1]+dc[1] c[2]+dc[2] |
+ //
+ // The perturbations are chosen such that
+ //
+ // da[2] > da[1] > da[0] > db[2] > db[1] > db[0] > dc[2] > dc[1] > dc[0]
+ //
+ // where each perturbation is so much smaller than the previous one that we
+ // don't even need to consider it unless the coefficients of all previous
+ // perturbations are zero. In fact, it is so small that we don't need to
+ // consider it unless the coefficient of all products of the previous
+ // perturbations are zero. For example, we don't need to consider the
+ // coefficient of db[1] unless the coefficient of db[2]*da[0] is zero.
+ //
+ // The follow code simply enumerates the coefficients of the perturbations
+ // (and products of perturbations) that appear in the determinant above, in
+ // order of decreasing perturbation magnitude. The first non-zero
+ // coefficient determines the sign of the result. The easiest way to
+ // enumerate the coefficients in the correct order is to pretend that each
+ // perturbation is some tiny value "eps" raised to a power of two:
+ //
+ // eps** 1 2 4 8 16 32 64 128 256
+ // da[2] da[1] da[0] db[2] db[1] db[0] dc[2] dc[1] dc[0]
+ //
+ // Essentially we can then just count in binary and test the corresponding
+ // subset of perturbations at each step. So for example, we must test the
+ // coefficient of db[2]*da[0] before db[1] because eps**12 > eps**16.
+ //
+ // Of course, not all products of these perturbations appear in the
+ // determinant above, since the determinant only contains the products of
+ // elements in distinct rows and columns. Thus we don't need to consider
+ // da[2]*da[1], db[1]*da[1], etc. Furthermore, sometimes different pairs of
+ // perturbations have the same coefficient in the determinant; for example,
+ // da[1]*db[0] and db[1]*da[0] have the same coefficient (c[2]). Therefore
+ // we only need to test this coefficient the first time we encounter it in
+ // the binary order above (which will be db[1]*da[0]).
+ //
+ // The sequence of tests below also appears in Table 4-ii of the paper
+ // referenced above, if you just want to look it up, with the following
+ // translations: [a,b,c] -> [i,j,k] and [0,1,2] -> [1,2,3]. Also note that
+ // some of the signs are different because the opposite cross product is
+ // used (e.g., B x C rather than C x B).
+
+ int det_sign = b_cross_c[2].sgn(); // da[2]
+ if (det_sign != 0) return det_sign;
+ det_sign = b_cross_c[1].sgn(); // da[1]
+ if (det_sign != 0) return det_sign;
+ det_sign = b_cross_c[0].sgn(); // da[0]
+ if (det_sign != 0) return det_sign;
+
+ det_sign = (c[0]*a[1] - c[1]*a[0]).sgn(); // db[2]
+ if (det_sign != 0) return det_sign;
+ det_sign = c[0].sgn(); // db[2] * da[1]
+ if (det_sign != 0) return det_sign;
+ det_sign = -(c[1].sgn()); // db[2] * da[0]
+ if (det_sign != 0) return det_sign;
+ det_sign = (c[2]*a[0] - c[0]*a[2]).sgn(); // db[1]
+ if (det_sign != 0) return det_sign;
+ det_sign = c[2].sgn(); // db[1] * da[0]
+ if (det_sign != 0) return det_sign;
+ // The following test is listed in the paper, but it is redundant because
+ // the previous tests guarantee that C == (0, 0, 0).
+ S2_DCHECK_EQ(0, (c[1]*a[2] - c[2]*a[1]).sgn()); // db[0]
+
+ det_sign = (a[0]*b[1] - a[1]*b[0]).sgn(); // dc[2]
+ if (det_sign != 0) return det_sign;
+ det_sign = -(b[0].sgn()); // dc[2] * da[1]
+ if (det_sign != 0) return det_sign;
+ det_sign = b[1].sgn(); // dc[2] * da[0]
+ if (det_sign != 0) return det_sign;
+ det_sign = a[0].sgn(); // dc[2] * db[1]
+ if (det_sign != 0) return det_sign;
+ return 1; // dc[2] * db[1] * da[0]
+}
+
+// Compute the determinant using exact arithmetic and/or symbolic
+// permutations. Requires that the three points are distinct.
+int ExactSign(const S2Point& a, const S2Point& b, const S2Point& c,
+ bool perturb) {
+ S2_DCHECK(a != b && b != c && c != a);
+
+ // Sort the three points in lexicographic order, keeping track of the sign
+ // of the permutation. (Each exchange inverts the sign of the determinant.)
+ int perm_sign = 1;
+ const S2Point *pa = &a, *pb = &b, *pc = &c;
+ using std::swap;
+ if (*pa > *pb) { swap(pa, pb); perm_sign = -perm_sign; }
+ if (*pb > *pc) { swap(pb, pc); perm_sign = -perm_sign; }
+ if (*pa > *pb) { swap(pa, pb); perm_sign = -perm_sign; }
+ S2_DCHECK(*pa < *pb && *pb < *pc);
+
+ // Construct multiple-precision versions of the sorted points and compute
+ // their exact 3x3 determinant.
+ Vector3_xf xa = Vector3_xf::Cast(*pa);
+ Vector3_xf xb = Vector3_xf::Cast(*pb);
+ Vector3_xf xc = Vector3_xf::Cast(*pc);
+ Vector3_xf xb_cross_xc = xb.CrossProd(xc);
+ ExactFloat det = xa.DotProd(xb_cross_xc);
+
+ // The precision of ExactFloat is high enough that the result should always
+ // be exact (no rounding was performed).
+ S2_DCHECK(!det.is_nan());
+ S2_DCHECK_LT(det.prec(), det.max_prec());
+
+ // If the exact determinant is non-zero, we're done.
+ int det_sign = det.sgn();
+ if (det_sign == 0 && perturb) {
+ // Otherwise, we need to resort to symbolic perturbations to resolve the
+ // sign of the determinant.
+ det_sign = SymbolicallyPerturbedSign(xa, xb, xc, xb_cross_xc);
+ S2_DCHECK_NE(0, det_sign);
+ }
+ return perm_sign * det_sign;
+}
+
+// ExpensiveSign() uses arbitrary-precision arithmetic and the "simulation of
+// simplicity" technique in order to be completely robust (i.e., to return
+// consistent results for all possible inputs).
+//
+// Below we define a floating-point type with enough precision so that it can
+// represent the exact determinant of any 3x3 matrix of floating-point
+// numbers. It uses ExactFloat, which is based on the OpenSSL Bignum library
+// and therefore has a permissive BSD-style license. (At one time we also
+// supported an option based on MPFR, but that has an LGPL license and is
+// therefore not suited for some applications.)
+
+using Vector3_xf = Vector3<ExactFloat>;
+
+int ExpensiveSign(const S2Point& a, const S2Point& b, const S2Point& c,
+ bool perturb) {
+ // Return zero if and only if two points are the same. This ensures (1).
+ if (a == b || b == c || c == a) return 0;
+
+ // Next we try recomputing the determinant still using floating-point
+ // arithmetic but in a more precise way. This is more expensive than the
+ // simple calculation done by TriageSign(), but it is still *much* cheaper
+ // than using arbitrary-precision arithmetic. This optimization is able to
+ // compute the correct determinant sign in virtually all cases except when
+ // the three points are truly collinear (e.g., three points on the equator).
+ int det_sign = StableSign(a, b, c);
+ if (det_sign != 0) return det_sign;
+
+ // TODO(ericv): Create a templated version of StableSign so that we can
+ // retry in "long double" precision before falling back to ExactFloat.
+
+ // TODO(ericv): Optimize ExactFloat so that it stores up to 32 bytes of
+ // mantissa inline (without requiring memory allocation).
+
+ // Otherwise fall back to exact arithmetic and symbolic permutations.
+ return ExactSign(a, b, c, perturb);
+}
+
+bool OrderedCCW(const S2Point& a, const S2Point& b, const S2Point& c,
+ const S2Point& o) {
+ // The last inequality below is ">" rather than ">=" so that we return true
+ // if A == B or B == C, and otherwise false if A == C. Recall that
+ // Sign(x,y,z) == -Sign(z,y,x) for all x,y,z.
+
+ int sum = 0;
+ if (Sign(b, o, a) >= 0) ++sum;
+ if (Sign(c, o, b) >= 0) ++sum;
+ if (Sign(a, o, c) > 0) ++sum;
+ return sum >= 2;
+}
+
+// Returns cos(XY), and sets "error" to the maximum error in the result.
+// REQUIRES: "x" and "y" satisfy S2::IsNormalized().
+inline double GetCosDistance(const S2Point& x, const S2Point& y,
+ double* error) {
+ double c = x.DotProd(y);
+ *error = 9.5 * DBL_ERR * fabs(c) + 1.5 * DBL_ERR;
+ return c;
+}
+
+// A high precision "long double" version of the function above.
+inline long double GetCosDistance(const Vector3_ld& x, const Vector3_ld& y,
+ long double* error) {
+ // With "long double" precision it is worthwhile to compensate for length
+ // errors in "x" and "y", since they are only unit length to within the
+ // precision of "double". (This would also reduce the error constant
+ // slightly in the method above but is not worth the additional effort.)
+ long double c = x.DotProd(y) / sqrt(x.Norm2() * y.Norm2());
+ *error = 7 * LD_ERR * fabs(c) + 1.5 * LD_ERR;
+ return c;
+}
+
+// Returns sin**2(XY), where XY is the angle between X and Y, and sets "error"
+// to the maximum error in the result.
+//
+// REQUIRES: "x" and "y" satisfy S2::IsNormalized().
+inline double GetSin2Distance(const S2Point& x, const S2Point& y,
+ double* error) {
+ // The (x-y).CrossProd(x+y) trick eliminates almost all of error due to "x"
+ // and "y" being not quite unit length. This method is extremely accurate
+ // for small distances; the *relative* error in the result is O(DBL_ERR) for
+ // distances as small as DBL_ERR.
+ S2Point n = (x - y).CrossProd(x + y);
+ double d2 = 0.25 * n.Norm2();
+ *error = ((21 + 4 * sqrt(3.0)) * DBL_ERR * d2 +
+ 32 * sqrt(3.0) * DBL_ERR * DBL_ERR * sqrt(d2) +
+ 768 * DBL_ERR * DBL_ERR * DBL_ERR * DBL_ERR);
+ return d2;
+}
+
+// A high precision "long double" version of the function above.
+inline long double GetSin2Distance(const Vector3_ld& x, const Vector3_ld& y,
+ long double* error) {
+ // In "long double" precision it is worthwhile to compensate for length
+ // errors in "x" and "y", since they are only unit length to within the
+ // precision of "double". Otherwise the "d2" error coefficient below would
+ // be (16 * DBL_ERR + (5 + 4 * sqrt(3)) * LD_ERR), which is much larger.
+ // (Dividing by the squared norms of "x" and "y" would also reduce the error
+ // constant slightly in the double-precision version, but this is not worth
+ // the additional effort.)
+ Vector3_ld n = (x - y).CrossProd(x + y);
+ long double d2 = 0.25 * n.Norm2() / (x.Norm2() * y.Norm2());
+ *error = ((13 + 4 * sqrt(3.0)) * LD_ERR * d2 +
+ 32 * sqrt(3.0) * DBL_ERR * LD_ERR * sqrt(d2) +
+ 768 * DBL_ERR * DBL_ERR * LD_ERR * LD_ERR);
+ return d2;
+}
+
+template <class T>
+int TriageCompareCosDistances(const Vector3<T>& x,
+ const Vector3<T>& a, const Vector3<T>& b) {
+ T cos_ax_error, cos_bx_error;
+ T cos_ax = GetCosDistance(a, x, &cos_ax_error);
+ T cos_bx = GetCosDistance(b, x, &cos_bx_error);
+ T diff = cos_ax - cos_bx;
+ T error = cos_ax_error + cos_bx_error;
+ return (diff > error) ? -1 : (diff < -error) ? 1 : 0;
+}
+
+template <class T>
+int TriageCompareSin2Distances(const Vector3<T>& x,
+ const Vector3<T>& a, const Vector3<T>& b) {
+ T sin2_ax_error, sin2_bx_error;
+ T sin2_ax = GetSin2Distance(a, x, &sin2_ax_error);
+ T sin2_bx = GetSin2Distance(b, x, &sin2_bx_error);
+ T diff = sin2_ax - sin2_bx;
+ T error = sin2_ax_error + sin2_bx_error;
+ return (diff > error) ? 1 : (diff < -error) ? -1 : 0;
+}
+
+int ExactCompareDistances(const Vector3_xf& x,
+ const Vector3_xf& a, const Vector3_xf& b) {
+ // This code produces the same result as though all points were reprojected
+ // to lie exactly on the surface of the unit sphere. It is based on testing
+ // whether x.DotProd(a.Normalize()) < x.DotProd(b.Normalize()), reformulated
+ // so that it can be evaluated using exact arithmetic.
+ ExactFloat cos_ax = x.DotProd(a);
+ ExactFloat cos_bx = x.DotProd(b);
+ // If the two values have different signs, we need to handle that case now
+ // before squaring them below.
+ int a_sign = cos_ax.sgn(), b_sign = cos_bx.sgn();
+ if (a_sign != b_sign) {
+ return (a_sign > b_sign) ? -1 : 1; // If cos(AX) > cos(BX), then AX < BX.
+ }
+ ExactFloat cmp = cos_bx * cos_bx * a.Norm2() - cos_ax * cos_ax * b.Norm2();
+ return a_sign * cmp.sgn();
+}
+
+// Given three points such that AX == BX (exactly), returns -1, 0, or +1
+// according whether AX < BX, AX == BX, or AX > BX after symbolic
+// perturbations are taken into account.
+int SymbolicCompareDistances(const S2Point& x,
+ const S2Point& a, const S2Point& b) {
+ // Our symbolic perturbation strategy is based on the following model.
+ // Similar to "simulation of simplicity", we assign a perturbation to every
+ // point such that if A < B, then the symbolic perturbation for A is much,
+ // much larger than the symbolic perturbation for B. We imagine that
+ // rather than projecting every point to lie exactly on the unit sphere,
+ // instead each point is positioned on its own tiny pedestal that raises it
+ // just off the surface of the unit sphere. This means that the distance AX
+ // is actually the true distance AX plus the (symbolic) heights of the
+ // pedestals for A and X. The pedestals are infinitesmally thin, so they do
+ // not affect distance measurements except at the two endpoints. If several
+ // points project to exactly the same point on the unit sphere, we imagine
+ // that they are placed on separate pedestals placed close together, where
+ // the distance between pedestals is much, much less than the height of any
+ // pedestal. (There are a finite number of S2Points, and therefore a finite
+ // number of pedestals, so this is possible.)
+ //
+ // If A < B, then A is on a higher pedestal than B, and therefore AX > BX.
+ return (a < b) ? 1 : (a > b) ? -1 : 0;
+}
+
+static int CompareSin2Distances(const S2Point& x,
+ const S2Point& a, const S2Point& b) {
+ int sign = TriageCompareSin2Distances(x, a, b);
+ if (sign != 0) return sign;
+ return TriageCompareSin2Distances(ToLD(x), ToLD(a), ToLD(b));
+}
+
+int CompareDistances(const S2Point& x, const S2Point& a, const S2Point& b) {
+ // We start by comparing distances using dot products (i.e., cosine of the
+ // angle), because (1) this is the cheapest technique, and (2) it is valid
+ // over the entire range of possible angles. (We can only use the sin^2
+ // technique if both angles are less than 90 degrees or both angles are
+ // greater than 90 degrees.)
+ int sign = TriageCompareCosDistances(x, a, b);
+ if (sign != 0) return sign;
+
+ // Optimization for (a == b) to avoid falling back to exact arithmetic.
+ if (a == b) return 0;
+
+ // It is much better numerically to compare distances using cos(angle) if
+ // the distances are near 90 degrees and sin^2(angle) if the distances are
+ // near 0 or 180 degrees. We only need to check one of the two angles when
+ // making this decision because the fact that the test above failed means
+ // that angles "a" and "b" are very close together.
+ double cos_ax = a.DotProd(x);
+ if (cos_ax > M_SQRT1_2) {
+ // Angles < 45 degrees.
+ sign = CompareSin2Distances(x, a, b);
+ } else if (cos_ax < -M_SQRT1_2) {
+ // Angles > 135 degrees. sin^2(angle) is decreasing in this range.
+ sign = -CompareSin2Distances(x, a, b);
+ } else {
+ // We've already tried double precision, so continue with "long double".
+ sign = TriageCompareCosDistances(ToLD(x), ToLD(a), ToLD(b));
+ }
+ if (sign != 0) return sign;
+ sign = ExactCompareDistances(ToExact(x), ToExact(a), ToExact(b));
+ if (sign != 0) return sign;
+ return SymbolicCompareDistances(x, a, b);
+}
+
+template <class T>
+int TriageCompareCosDistance(const Vector3<T>& x, const Vector3<T>& y, T r2) {
+ constexpr T T_ERR = rounding_epsilon<T>();
+ T cos_xy_error;
+ T cos_xy = GetCosDistance(x, y, &cos_xy_error);
+ T cos_r = 1 - 0.5 * r2;
+ T cos_r_error = 2 * T_ERR * cos_r;
+ T diff = cos_xy - cos_r;
+ T error = cos_xy_error + cos_r_error;
+ return (diff > error) ? -1 : (diff < -error) ? 1 : 0;
+}
+
+template <class T>
+int TriageCompareSin2Distance(const Vector3<T>& x, const Vector3<T>& y, T r2) {
+ S2_DCHECK_LT(r2, 2.0); // Only valid for distance limits < 90 degrees.
+
+ constexpr T T_ERR = rounding_epsilon<T>();
+ T sin2_xy_error;
+ T sin2_xy = GetSin2Distance(x, y, &sin2_xy_error);
+ T sin2_r = r2 * (1 - 0.25 * r2);
+ T sin2_r_error = 3 * T_ERR * sin2_r;
+ T diff = sin2_xy - sin2_r;
+ T error = sin2_xy_error + sin2_r_error;
+ return (diff > error) ? 1 : (diff < -error) ? -1 : 0;
+}
+
+int ExactCompareDistance(const Vector3_xf& x, const Vector3_xf& y,
+ const ExactFloat& r2) {
+ // This code produces the same result as though all points were reprojected
+ // to lie exactly on the surface of the unit sphere. It is based on
+ // comparing the cosine of the angle XY (when both points are projected to
+ // lie exactly on the sphere) to the given threshold.
+ ExactFloat cos_xy = x.DotProd(y);
+ ExactFloat cos_r = 1 - 0.5 * r2;
+ // If the two values have different signs, we need to handle that case now
+ // before squaring them below.
+ int xy_sign = cos_xy.sgn(), r_sign = cos_r.sgn();
+ if (xy_sign != r_sign) {
+ return (xy_sign > r_sign) ? -1 : 1; // If cos(XY) > cos(r), then XY < r.
+ }
+ ExactFloat cmp = cos_r * cos_r * x.Norm2() * y.Norm2() - cos_xy * cos_xy;
+ return xy_sign * cmp.sgn();
+}
+
+int CompareDistance(const S2Point& x, const S2Point& y, S1ChordAngle r) {
+ // As with CompareDistances(), we start by comparing dot products because
+ // the sin^2 method is only valid when the distance XY and the limit "r" are
+ // both less than 90 degrees.
+ int sign = TriageCompareCosDistance(x, y, r.length2());
+ if (sign != 0) return sign;
+
+ // Unlike with CompareDistances(), it's not worth using the sin^2 method
+ // when the distance limit is near 180 degrees because the S1ChordAngle
+ // representation itself has has a rounding error of up to 2e-8 radians for
+ // distances near 180 degrees.
+ if (r < k45Degrees) {
+ sign = TriageCompareSin2Distance(x, y, r.length2());
+ if (sign != 0) return sign;
+ sign = TriageCompareSin2Distance(ToLD(x), ToLD(y), ToLD(r.length2()));
+ } else {
+ sign = TriageCompareCosDistance(ToLD(x), ToLD(y), ToLD(r.length2()));
+ }
+ if (sign != 0) return sign;
+ return ExactCompareDistance(ToExact(x), ToExact(y), r.length2());
+}
+
+// Helper function that compares the distance XY against the squared chord
+// distance "r2" using the given precision "T".
+template <class T>
+int TriageCompareDistance(const Vector3<T>& x, const Vector3<T>& y, T r2) {
+ // The Sin2 method is much more accurate for small distances, but it is only
+ // valid when the actual distance and the distance limit are both less than
+ // 90 degrees. So we always start with the Cos method.
+ int sign = TriageCompareCosDistance(x, y, r2);
+ if (sign == 0 && r2 < k45Degrees.length2()) {
+ sign = TriageCompareSin2Distance(x, y, r2);
+ }
+ return sign;
+}
+
+// Helper function that returns "a0" or "a1", whichever is closer to "x".
+// Also returns the squared distance from the returned point to "x" in "ax2".
+template <class T>
+inline Vector3<T> GetClosestVertex(const Vector3<T>& x, const Vector3<T>& a0,
+ const Vector3<T>& a1, T* ax2) {
+ T a0x2 = (a0 - x).Norm2();
+ T a1x2 = (a1 - x).Norm2();
+ if (a0x2 < a1x2 || (a0x2 == a1x2 && a0 < a1)) {
+ *ax2 = a0x2;
+ return a0;
+ } else {
+ *ax2 = a1x2;
+ return a1;
+ }
+}
+
+// Helper function that returns -1, 0, or +1 according to whether the distance
+// from "x" to the great circle through (a0, a1) is less than, equal to, or
+// greater than the given squared chord length "r2". This method computes the
+// squared sines of the distances involved, which is more accurate when the
+// distances are small (less than 45 degrees).
+//
+// The remaining parameters are functions of (a0, a1) and are passed in
+// because they have already been computed: n = (a0 - a1) x (a0 + a1),
+// n1 = n.Norm(), and n2 = n.Norm2().
+template <class T>
+int TriageCompareLineSin2Distance(const Vector3<T>& x, const Vector3<T>& a0,
+ const Vector3<T>& a1, T r2,
+ const Vector3<T>& n, T n1, T n2) {
+ constexpr T T_ERR = rounding_epsilon<T>();
+
+ // The minimum distance is to a point on the edge interior. Since the true
+ // distance to the edge is always less than 90 degrees, we can return
+ // immediately if the limit is 90 degrees or larger.
+ if (r2 >= 2.0) return -1; // distance < limit
+
+ // Otherwise we compute sin^2(distance to edge) to get the best accuracy
+ // when the distance limit is small (e.g., S2::kIntersectionError).
+ T n2sin2_r = n2 * r2 * (1 - 0.25 * r2);
+ T n2sin2_r_error = 6 * T_ERR * n2sin2_r;
+ T ax2, xDn = (x - GetClosestVertex(x, a0, a1, &ax2)).DotProd(n);
+ T xDn2 = xDn * xDn;
+ const T c1 = (((3.5 + 2 * sqrt(3.0)) * n1 + 32 * sqrt(3.0) * DBL_ERR) *
+ T_ERR * sqrt(ax2));
+ T xDn2_error = 4 * T_ERR * xDn2 + (2 * fabs(xDn) + c1) * c1;
+
+ // If we are using extended precision, then it is worthwhile to recompute
+ // the length of X more accurately. Otherwise we use the fact that X is
+ // guaranteed to be unit length to with a tolerance of 4 * DBL_ERR.
+ if (T_ERR < DBL_ERR) {
+ n2sin2_r *= x.Norm2();
+ n2sin2_r_error += 4 * T_ERR * n2sin2_r;
+ } else {
+ n2sin2_r_error += 8 * DBL_ERR * n2sin2_r;
+ }
+ T diff = xDn2 - n2sin2_r;
+ T error = xDn2_error + n2sin2_r_error;
+ return (diff > error) ? 1 : (diff < -error) ? -1 : 0;
+}
+
+// Like TriageCompareLineSin2Distance, but this method computes the squared
+// cosines of the distances involved. It is more accurate when the distances
+// are large (greater than 45 degrees).
+template <class T>
+int TriageCompareLineCos2Distance(const Vector3<T>& x, const Vector3<T>& a0,
+ const Vector3<T>& a1, T r2,
+ const Vector3<T>& n, T n1, T n2) {
+ constexpr T T_ERR = rounding_epsilon<T>();
+
+ // The minimum distance is to a point on the edge interior. Since the true
+ // distance to the edge is always less than 90 degrees, we can return
+ // immediately if the limit is 90 degrees or larger.
+ if (r2 >= 2.0) return -1; // distance < limit
+
+ // Otherwise we compute cos^2(distance to edge).
+ T cos_r = 1 - 0.5 * r2;
+ T n2cos2_r = n2 * cos_r * cos_r;
+ T n2cos2_r_error = 7 * T_ERR * n2cos2_r;
+
+ // The length of M = X.CrossProd(N) is the cosine of the distance.
+ T m2 = x.CrossProd(n).Norm2();
+ T m1 = sqrt(m2);
+ T m1_error = ((1 + 8 / sqrt(3.0)) * n1 + 32 * sqrt(3.0) * DBL_ERR) * T_ERR;
+ T m2_error = 3 * T_ERR * m2 + (2 * m1 + m1_error) * m1_error;
+
+ // If we are using extended precision, then it is worthwhile to recompute
+ // the length of X more accurately. Otherwise we use the fact that X is
+ // guaranteed to be unit length to within a tolerance of 4 * DBL_ERR.
+ if (T_ERR < DBL_ERR) {
+ n2cos2_r *= x.Norm2();
+ n2cos2_r_error += 4 * T_ERR * n2cos2_r;
+ } else {
+ n2cos2_r_error += 8 * DBL_ERR * n2cos2_r;
+ }
+ T diff = m2 - n2cos2_r;
+ T error = m2_error + n2cos2_r_error;
+ return (diff > error) ? -1 : (diff < -error) ? 1 : 0;
+}
+
+template <class T>
+inline int TriageCompareLineDistance(const Vector3<T>& x, const Vector3<T>& a0,
+ const Vector3<T>& a1, T r2,
+ const Vector3<T>& n, T n1, T n2) {
+ if (r2 < k45Degrees.length2()) {
+ return TriageCompareLineSin2Distance(x, a0, a1, r2, n, n1, n2);
+ } else {
+ return TriageCompareLineCos2Distance(x, a0, a1, r2, n, n1, n2);
+ }
+}
+
+template <class T>
+int TriageCompareEdgeDistance(const Vector3<T>& x, const Vector3<T>& a0,
+ const Vector3<T>& a1, T r2) {
+ constexpr T T_ERR = rounding_epsilon<T>();
+
+ // First we need to decide whether the closest point is an edge endpoint or
+ // somewhere in the interior. To determine this we compute a plane
+ // perpendicular to (a0, a1) that passes through X. Letting M be the normal
+ // to this plane, the closest point is in the edge interior if and only if
+ // a0.M < 0 and a1.M > 0. Note that we can use "<" rather than "<=" because
+ // if a0.M or a1.M is zero exactly then it doesn't matter which code path we
+ // follow (since the distance to an endpoint and the distance to the edge
+ // interior are exactly the same in this case).
+ Vector3<T> n = (a0 - a1).CrossProd(a0 + a1);
+ Vector3<T> m = n.CrossProd(x);
+ // For better accuracy when the edge (a0,a1) is very short, we subtract "x"
+ // before computing the dot products with M.
+ Vector3<T> a0_dir = a0 - x;
+ Vector3<T> a1_dir = a1 - x;
+ T a0_sign = a0_dir.DotProd(m);
+ T a1_sign = a1_dir.DotProd(m);
+ T n2 = n.Norm2();
+ T n1 = sqrt(n2);
+ T n1_error = ((3.5 + 8 / sqrt(3.0)) * n1 + 32 * sqrt(3.0) * DBL_ERR) * T_ERR;
+ T a0_sign_error = n1_error * a0_dir.Norm();
+ T a1_sign_error = n1_error * a1_dir.Norm();
+ if (fabs(a0_sign) < a0_sign_error || fabs(a1_sign) < a1_sign_error) {
+ // It is uncertain whether minimum distance is to an edge vertex or to the
+ // edge interior. We handle this by computing both distances and checking
+ // whether they yield the same result.
+ int vertex_sign = min(TriageCompareDistance(x, a0, r2),
+ TriageCompareDistance(x, a1, r2));
+ int line_sign = TriageCompareLineDistance(x, a0, a1, r2, n, n1, n2);
+ return (vertex_sign == line_sign) ? line_sign : 0;
+ }
+ if (a0_sign >= 0 || a1_sign <= 0) {
+ // The minimum distance is to an edge endpoint.
+ return min(TriageCompareDistance(x, a0, r2),
+ TriageCompareDistance(x, a1, r2));
+ } else {
+ // The minimum distance is to the edge interior.
+ return TriageCompareLineDistance(x, a0, a1, r2, n, n1, n2);
+ }
+}
+
+// REQUIRES: the closest point to "x" is in the interior of edge (a0, a1).
+int ExactCompareLineDistance(const Vector3_xf& x, const Vector3_xf& a0,
+ const Vector3_xf& a1, const ExactFloat& r2) {
+ // Since we are given that the closest point is in the edge interior, the
+ // true distance is always less than 90 degrees (which corresponds to a
+ // squared chord length of 2.0).
+ if (r2 >= 2.0) return -1; // distance < limit
+
+ // Otherwise compute the edge normal
+ Vector3_xf n = a0.CrossProd(a1);
+ ExactFloat sin_d = x.DotProd(n);
+ ExactFloat sin2_r = r2 * (1 - 0.25 * r2);
+ ExactFloat cmp = sin_d * sin_d - sin2_r * x.Norm2() * n.Norm2();
+ return cmp.sgn();
+}
+
+int ExactCompareEdgeDistance(const S2Point& x, const S2Point& a0,
+ const S2Point& a1, S1ChordAngle r) {
+ // Even if previous calculations were uncertain, we might not need to do
+ // *all* the calculations in exact arithmetic here. For example it may be
+ // easy to determine whether "x" is closer to an endpoint or the edge
+ // interior. The only calculation where we always use exact arithmetic is
+ // when measuring the distance to the extended line (great circle) through
+ // "a0" and "a1", since it is virtually certain that the previous floating
+ // point calculations failed in that case.
+ //
+ // CompareEdgeDirections also checks that no edge has antipodal endpoints.
+ if (CompareEdgeDirections(a0, a1, a0, x) > 0 &&
+ CompareEdgeDirections(a0, a1, x, a1) > 0) {
+ // The closest point to "x" is along the interior of the edge.
+ return ExactCompareLineDistance(ToExact(x), ToExact(a0), ToExact(a1),
+ r.length2());
+ } else {
+ // The closest point to "x" is one of the edge endpoints.
+ return min(CompareDistance(x, a0, r), CompareDistance(x, a1, r));
+ }
+}
+
+int CompareEdgeDistance(const S2Point& x, const S2Point& a0, const S2Point& a1,
+ S1ChordAngle r) {
+ // Check that the edge does not consist of antipodal points. (This catches
+ // the most common case -- the full test is in ExactCompareEdgeDistance.)
+ S2_DCHECK_NE(a0, -a1);
+
+ int sign = TriageCompareEdgeDistance(x, a0, a1, r.length2());
+ if (sign != 0) return sign;
+
+ // Optimization for the case where the edge is degenerate.
+ if (a0 == a1) return CompareDistance(x, a0, r);
+
+ sign = TriageCompareEdgeDistance(ToLD(x), ToLD(a0), ToLD(a1),
+ ToLD(r.length2()));
+ if (sign != 0) return sign;
+ return ExactCompareEdgeDistance(x, a0, a1, r);
+}
+
+template <class T>
+int TriageCompareEdgeDirections(
+ const Vector3<T>& a0, const Vector3<T>& a1,
+ const Vector3<T>& b0, const Vector3<T>& b1) {
+ constexpr T T_ERR = rounding_epsilon<T>();
+ Vector3<T> na = (a0 - a1).CrossProd(a0 + a1);
+ Vector3<T> nb = (b0 - b1).CrossProd(b0 + b1);
+ T na_len = na.Norm(), nb_len = nb.Norm();
+ T cos_ab = na.DotProd(nb);
+ T cos_ab_error = ((5 + 4 * sqrt(3.0)) * na_len * nb_len +
+ 32 * sqrt(3.0) * DBL_ERR * (na_len + nb_len)) * T_ERR;
+ return (cos_ab > cos_ab_error) ? 1 : (cos_ab < -cos_ab_error) ? -1 : 0;
+}
+
+bool ArePointsLinearlyDependent(const Vector3_xf& x, const Vector3_xf& y) {
+ Vector3_xf n = x.CrossProd(y);
+ return n[0].sgn() == 0 && n[1].sgn() == 0 && n[2].sgn() == 0;
+}
+
+bool ArePointsAntipodal(const Vector3_xf& x, const Vector3_xf& y) {
+ return ArePointsLinearlyDependent(x, y) && x.DotProd(y).sgn() < 0;
+}
+
+int ExactCompareEdgeDirections(const Vector3_xf& a0, const Vector3_xf& a1,
+ const Vector3_xf& b0, const Vector3_xf& b1) {
+ S2_DCHECK(!ArePointsAntipodal(a0, a1));
+ S2_DCHECK(!ArePointsAntipodal(b0, b1));
+ return a0.CrossProd(a1).DotProd(b0.CrossProd(b1)).sgn();
+}
+
+int CompareEdgeDirections(const S2Point& a0, const S2Point& a1,
+ const S2Point& b0, const S2Point& b1) {
+ // Check that no edge consists of antipodal points. (This catches the most
+ // common case -- the full test is in ExactCompareEdgeDirections.)
+ S2_DCHECK_NE(a0, -a1);
+ S2_DCHECK_NE(b0, -b1);
+
+ int sign = TriageCompareEdgeDirections(a0, a1, b0, b1);
+ if (sign != 0) return sign;
+
+ // Optimization for the case where either edge is degenerate.
+ if (a0 == a1 || b0 == b1) return 0;
+
+ sign = TriageCompareEdgeDirections(ToLD(a0), ToLD(a1), ToLD(b0), ToLD(b1));
+ if (sign != 0) return sign;
+ return ExactCompareEdgeDirections(ToExact(a0), ToExact(a1),
+ ToExact(b0), ToExact(b1));
+}
+
+// If triangle ABC has positive sign, returns its circumcenter. If ABC has
+// negative sign, returns the negated circumcenter.
+template <class T>
+Vector3<T> GetCircumcenter(const Vector3<T>& a, const Vector3<T>& b,
+ const Vector3<T>& c, T* error) {
+ constexpr T T_ERR = rounding_epsilon<T>();
+
+ // We compute the circumcenter using the intersection of the perpendicular
+ // bisectors of AB and BC. The formula is essentially
+ //
+ // Z = ((A x B) x (A + B)) x ((B x C) x (B + C)),
+ //
+ // except that we compute the cross product (A x B) as (A - B) x (A + B)
+ // (and similarly for B x C) since this is much more stable when the inputs
+ // are unit vectors.
+ Vector3<T> ab_diff = a - b, ab_sum = a + b;
+ Vector3<T> bc_diff = b - c, bc_sum = b + c;
+ Vector3<T> nab = ab_diff.CrossProd(ab_sum);
+ T nab_len = nab.Norm();
+ T ab_len = ab_diff.Norm();
+ Vector3<T> nbc = bc_diff.CrossProd(bc_sum);
+ T nbc_len = nbc.Norm();
+ T bc_len = bc_diff.Norm();
+ Vector3<T> mab = nab.CrossProd(ab_sum);
+ Vector3<T> mbc = nbc.CrossProd(bc_sum);
+ *error = (((16 + 24 * sqrt(3.0)) * T_ERR +
+ 8 * DBL_ERR * (ab_len + bc_len)) * nab_len * nbc_len +
+ 128 * sqrt(3.0) * DBL_ERR * T_ERR * (nab_len + nbc_len) +
+ 3 * 4096 * DBL_ERR * DBL_ERR * T_ERR * T_ERR);
+ return mab.CrossProd(mbc);
+}
+
+template <class T>
+int TriageEdgeCircumcenterSign(const Vector3<T>& x0, const Vector3<T>& x1,
+ const Vector3<T>& a, const Vector3<T>& b,
+ const Vector3<T>& c, int abc_sign) {
+ constexpr T T_ERR = rounding_epsilon<T>();
+
+ // Compute the circumcenter Z of triangle ABC, and then test which side of
+ // edge X it lies on.
+ T z_error;
+ Vector3<T> z = GetCircumcenter(a, b, c, &z_error);
+ Vector3<T> nx = (x0 - x1).CrossProd(x0 + x1);
+ // If the sign of triangle ABC is negative, then we have computed -Z and the
+ // result should be negated.
+ T result = abc_sign * nx.DotProd(z);
+
+ T z_len = z.Norm();
+ T nx_len = nx.Norm();
+ T nx_error = ((1 + 2 * sqrt(3.0)) * nx_len + 32 * sqrt(3.0) * DBL_ERR) * T_ERR;
+ T result_error = ((3 * T_ERR * nx_len + nx_error) * z_len + z_error * nx_len);
+ return (result > result_error) ? 1 : (result < -result_error) ? -1 : 0;
+}
+
+int ExactEdgeCircumcenterSign(const Vector3_xf& x0, const Vector3_xf& x1,
+ const Vector3_xf& a, const Vector3_xf& b,
+ const Vector3_xf& c, int abc_sign) {
+ // Return zero if the edge X is degenerate. (Also see the comments in
+ // SymbolicEdgeCircumcenterSign.)
+ if (ArePointsLinearlyDependent(x0, x1)) {
+ S2_DCHECK_GT(x0.DotProd(x1), 0); // Antipodal edges not allowed.
+ return 0;
+ }
+ // The simplest predicate for testing whether the sign is positive is
+ //
+ // (1) (X0 x X1) . (|C|(A x B) + |A|(B x C) + |B|(C x A)) > 0
+ //
+ // where |A| denotes A.Norm() and the expression after the "." represents
+ // the circumcenter of triangle ABC. (This predicate is terrible from a
+ // numerical accuracy point of view, but that doesn't matter since we are
+ // going to use exact arithmetic.) This predicate also assumes that
+ // triangle ABC is CCW (positive sign); we correct for that below.
+ //
+ // The only problem with evaluating this inequality is that computing |A|,
+ // |B| and |C| requires square roots. To avoid this problem we use the
+ // standard technique of rearranging the inequality to isolate at least one
+ // square root and then squaring both sides. We need to repeat this process
+ // twice in order to eliminate all the square roots, which leads to a
+ // polynomial predicate of degree 20 in the input arguments.
+ //
+ // Rearranging (1) we get
+ //
+ // (X0 x X1) . (|C|(A x B) + |A|(B x C)) > |B|(X0 x X1) . (A x C)
+ //
+ // Before squaring we need to check the sign of each side. If the signs are
+ // different then we know the result without squaring, and if the signs are
+ // both negative then after squaring both sides we need to invert the
+ // result. Define
+ //
+ // dAB = (X0 x X1) . (A x B)
+ // dBC = (X0 x X1) . (B x C)
+ // dCA = (X0 x X1) . (C x A)
+ //
+ // Then we can now write the inequality above as
+ //
+ // (2) |C| dAB + |A| dBC > -|B| dCA
+ //
+ // The RHS of (2) is positive if dCA < 0, and the LHS of (2) is positive if
+ // (|C| dAB + |A| dBC) > 0. Since the LHS has square roots, we need to
+ // eliminate them using the same process. Rewriting the LHS as
+ //
+ // (3) |C| dAB > -|A| dBC
+ //
+ // we again need to check the signs of both sides. Let's start with that.
+ // We also precompute the following values because they are used repeatedly
+ // when squaring various expressions below:
+ //
+ // abc2 = |A|^2 dBC^2
+ // bca2 = |B|^2 dCA^2
+ // cab2 = |C|^2 dAB^2
+ Vector3_xf nx = x0.CrossProd(x1);
+ ExactFloat dab = nx.DotProd(a.CrossProd(b));
+ ExactFloat dbc = nx.DotProd(b.CrossProd(c));
+ ExactFloat dca = nx.DotProd(c.CrossProd(a));
+ ExactFloat abc2 = a.Norm2() * (dbc * dbc);
+ ExactFloat bca2 = b.Norm2() * (dca * dca);
+ ExactFloat cab2 = c.Norm2() * (dab * dab);
+
+ // If the two sides of (3) have different signs (including the case where
+ // one side is zero) then we know the result. Also, if both sides are zero
+ // then we know the result. The following logic encodes this.
+ int lhs3_sgn = dab.sgn(), rhs3_sgn = -dbc.sgn();
+ int lhs2_sgn = max(-1, min(1, lhs3_sgn - rhs3_sgn));
+ if (lhs2_sgn == 0 && lhs3_sgn != 0) {
+ // Both sides of (3) have the same non-zero sign, so square both sides.
+ // If both sides were negative then invert the result.
+ lhs2_sgn = (cab2 - abc2).sgn() * lhs3_sgn;
+ }
+ // Now if the two sides of (2) have different signs then we know the result
+ // of this entire function.
+ int rhs2_sgn = -dca.sgn();
+ int result = max(-1, min(1, lhs2_sgn - rhs2_sgn));
+ if (result == 0 && lhs2_sgn != 0) {
+ // Both sides of (2) have the same non-zero sign, so square both sides.
+ // (If both sides were negative then we invert the result below.)
+ // This gives
+ //
+ // |C|^2 dAB^2 + |A|^2 dBC^2 + 2 |A| |C| dAB dBC > |B|^2 dCA^2
+ //
+ // This expression still has square roots (|A| and |C|), so we rewrite as
+ //
+ // (4) 2 |A| |C| dAB dBC > |B|^2 dCA^2 - |C|^2 dAB^2 - |A|^2 dBC^2 .
+ //
+ // Again, if the two sides have different signs then we know the result.
+ int lhs4_sgn = dab.sgn() * dbc.sgn();
+ ExactFloat rhs4 = bca2 - cab2 - abc2;
+ result = max(-1, min(1, lhs4_sgn - rhs4.sgn()));
+ if (result == 0 && lhs4_sgn != 0) {
+ // Both sides of (4) have the same non-zero sign, so square both sides.
+ // If both sides were negative then invert the result.
+ result = (4 * abc2 * cab2 - rhs4 * rhs4).sgn() * lhs4_sgn;
+ }
+ // Correct the sign if both sides of (2) were negative.
+ result *= lhs2_sgn;
+ }
+ // If the sign of triangle ABC is negative, then we have computed -Z and the
+ // result should be negated.
+ return abc_sign * result;
+}
+
+// Like Sign, except this method does not use symbolic perturbations when
+// the input points are exactly coplanar with the origin (i.e., linearly
+// dependent). Clients should never use this method, but it is useful here in
+// order to implement the combined pedestal/axis-aligned perturbation scheme
+// used by some methods (such as EdgeCircumcenterSign).
+int UnperturbedSign(const S2Point& a, const S2Point& b, const S2Point& c) {
+ int sign = TriageSign(a, b, c, a.CrossProd(b));
+ if (sign == 0) sign = ExpensiveSign(a, b, c, false /*perturb*/);
+ return sign;
+}
+
+// Given arguments such that ExactEdgeCircumcenterSign(x0, x1, a, b, c) == 0,
+// returns the value of Sign(X0, X1, Z) (where Z is the circumcenter of
+// triangle ABC) after symbolic perturbations are taken into account. The
+// result is zero only if X0 == X1, A == B, B == C, or C == A. (It is nonzero
+// if these pairs are exactly proportional to each other but not equal.)
+int SymbolicEdgeCircumcenterSign(
+ const S2Point& x0, const S2Point& x1,
+ const S2Point& a_arg, const S2Point& b_arg, const S2Point& c_arg) {
+ // We use the same perturbation strategy as SymbolicCompareDistances. Note
+ // that pedestal perturbations of X0 and X1 do not affect the result,
+ // because Sign(X0, X1, Z) does not change when its arguments are scaled
+ // by a positive factor. Therefore we only need to consider A, B, C.
+ // Suppose that A is the smallest lexicographically and therefore has the
+ // largest perturbation. This has the effect of perturbing the circumcenter
+ // of ABC slightly towards A, and since the circumcenter Z was previously
+ // exactly collinear with edge X, this implies that after the perturbation
+ // Sign(X0, X1, Z) == UnperturbedSign(X0, X1, A). (We want the result
+ // to be zero if X0, X1, and A are linearly dependent, rather than using
+ // symbolic perturbations, because these perturbations are defined to be
+ // much, much smaller than the pedestal perturbation of B and C that are
+ // considered below.)
+ //
+ // If A is also exactly collinear with edge X, then we move on to the next
+ // smallest point lexicographically out of {B, C}. It is easy to see that
+ // as long as A, B, C are all distinct, one of these three Sign calls
+ // will be nonzero, because if A, B, C are all distinct and collinear with
+ // edge X then their circumcenter Z coincides with the normal of X, and
+ // therefore Sign(X0, X1, Z) is nonzero.
+ //
+ // This function could be extended to handle the case where X0 and X1 are
+ // linearly dependent as follows. First, suppose that every point has both
+ // a pedestal peturbation as described above, and also the three
+ // axis-aligned perturbations described in the "Simulation of Simplicity"
+ // paper, where all pedestal perturbations are defined to be much, much
+ // larger than any axis-aligned perturbation. Note that since pedestal
+ // perturbations have no effect on Sign, we can use this model for *all*
+ // the S2 predicates, which ensures that all the various predicates are
+ // fully consistent with each other.
+ //
+ // With this model, the strategy described above yields the correct result
+ // unless X0 and X1 are exactly linearly dependent. When that happens, then
+ // no perturbation (pedestal or axis-aligned) of A,B,C affects the result,
+ // and no pedestal perturbation of X0 or X1 affects the result, therefore we
+ // need to consider the smallest axis-aligned perturbation of X0 or X1. The
+ // first perturbation that makes X0 and X1 linearly independent yields the
+ // result. Supposing that X0 < X1, this is the perturbation of X0[2] unless
+ // both points are multiples of [0, 0, 1], in which case it is the
+ // perturbation of X0[1]. The sign test can be implemented by computing the
+ // perturbed cross product of X0 and X1 and taking the dot product with the
+ // exact value of Z. For example if X0[2] is perturbed, the perturbed cross
+ // product is proportional to (0, 0, 1) x X1 = (-X1[1], x1[0], 0). Note
+ // that if the dot product with Z is exactly zero, then it is still
+ // necessary to fall back to pedestal perturbations of A, B, C, but one of
+ // these perturbations is now guaranteed to succeed.
+
+ // If any two triangle vertices are equal, the result is zero.
+ if (a_arg == b_arg || b_arg == c_arg || c_arg == a_arg) return 0;
+
+ // Sort A, B, C in lexicographic order.
+ const S2Point *a = &a_arg, *b = &b_arg, *c = &c_arg;
+ if (*b < *a) std::swap(a, b);
+ if (*c < *b) std::swap(b, c);
+ if (*b < *a) std::swap(a, b);
+
+ // Now consider the perturbations in decreasing order of size.
+ int sign = UnperturbedSign(x0, x1, *a);
+ if (sign != 0) return sign;
+ sign = UnperturbedSign(x0, x1, *b);
+ if (sign != 0) return sign;
+ return UnperturbedSign(x0, x1, *c);
+}
+
+int EdgeCircumcenterSign(const S2Point& x0, const S2Point& x1,
+ const S2Point& a, const S2Point& b,
+ const S2Point& c) {
+ // Check that the edge does not consist of antipodal points. (This catches
+ // the most common case -- the full test is in ExactEdgeCircumcenterSign.)
+ S2_DCHECK_NE(x0, -x1);
+
+ int abc_sign = Sign(a, b, c);
+ int sign = TriageEdgeCircumcenterSign(x0, x1, a, b, c, abc_sign);
+ if (sign != 0) return sign;
+
+ // Optimization for the cases that are going to return zero anyway, in order
+ // to avoid falling back to exact arithmetic.
+ if (x0 == x1 || a == b || b == c || c == a) return 0;
+
+ sign = TriageEdgeCircumcenterSign(
+ ToLD(x0), ToLD(x1), ToLD(a), ToLD(b), ToLD(c), abc_sign);
+ if (sign != 0) return sign;
+ sign = ExactEdgeCircumcenterSign(
+ ToExact(x0), ToExact(x1), ToExact(a), ToExact(b), ToExact(c), abc_sign);
+ if (sign != 0) return sign;
+
+ // Unlike the other methods, SymbolicEdgeCircumcenterSign does not depend
+ // on the sign of triangle ABC.
+ return SymbolicEdgeCircumcenterSign(x0, x1, a, b, c);
+}
+
+template <class T>
+Excluded TriageVoronoiSiteExclusion(const Vector3<T>& a, const Vector3<T>& b,
+ const Vector3<T>& x0, const Vector3<T>& x1,
+ T r2) {
+ constexpr T T_ERR = rounding_epsilon<T>();
+
+ // Define the "coverage disc" of a site S to be the disc centered at S with
+ // radius r (i.e., squared chord angle length r2). Similarly, define the
+ // "coverage interval" of S along an edge X to be the intersection of X with
+ // the coverage disc of S. The coverage interval can be represented as the
+ // point at the center of the interval and an angle that measures the
+ // semi-width or "radius" of the interval.
+ //
+ // To test whether site A excludes site B along the input edge X, we test
+ // whether the coverage interval of A contains the coverage interval of B.
+ // Let "ra" and "rb" be the radii (semi-widths) of the two intervals, and
+ // let "d" be the angle between their center points. Then "a" properly
+ // contains "b" if (ra - rb > d), and "b" contains "a" if (rb - ra > d).
+ // Note that only one of these conditions can be true. Therefore we can
+ // determine whether one site excludes the other by checking whether
+ //
+ // (1) |rb - ra| > d
+ //
+ // and use the sign of (rb - ra) to determine which site is excluded.
+ //
+ // The actual code is based on the following. Let A1 and B1 be the unit
+ // vectors A and B scaled by cos(r) (these points are inside the sphere).
+ // The planes perpendicular to OA1 and OA2 cut off two discs of radius r
+ // around A and B. Now consider the two lines (inside the sphere) where
+ // these planes intersect the plane containing the input edge X, and let A2
+ // and B2 be the points on these lines that are closest to A and B. The
+ // coverage intervals of A and B can be represented as an interval along
+ // each of these lines, centered at A2 and B2. Let P1 and P2 be the
+ // endpoints of the coverage interval for A, and let Q1 and Q2 be the
+ // endpoints of the coverage interval for B. We can view each coverage
+ // interval as either a chord through the sphere's interior, or as a segment
+ // of the original edge X (by projecting the chord onto the sphere's
+ // surface).
+ //
+ // To check whether B's interval is contained by A's interval, we test
+ // whether both endpoints of B's interval (Q1 and Q2) are contained by A's
+ // interval. E.g., we could test whether Qi.DotProd(A2) > A2.Norm2().
+ //
+ // However rather than constructing the actual points A1, A2, and so on, it
+ // turns out to be more efficient to compute the sines and cosines
+ // ("components") of the various angles and then use trigonometric
+ // identities. Predicate (1) can be expressed as
+ //
+ // |sin(rb - ra)| > sin(d)
+ //
+ // provided that |d| <= Pi/2 (which must be checked), and then expanded to
+ //
+ // (2) |sin(rb) cos(ra) - sin(ra) cos(rb)| > sin(d) .
+ //
+ // The components of the various angles can be expressed using dot and cross
+ // products based on the construction above:
+ //
+ // sin(ra) = sqrt(sin^2(r) |a|^2 |n|^2 - |a.n|^2) / |aXn|
+ // cos(ra) = cos(r) |a| |n| / |aXn|
+ // sin(rb) = sqrt(sin^2(r) |b|^2 |n|^2 - |b.n|^2) / |bXn|
+ // cos(rb) = cos(r) |b| |n| / |bXn|
+ // sin(d) = (aXb).n |n| / (|aXn| |bXn|)
+ // cos(d) = (aXn).(bXn) / (|aXn| |bXn|)
+ //
+ // Also, the squared chord length r2 is equal to 4 * sin^2(r / 2), which
+ // yields the following relationships:
+ //
+ // sin(r) = sqrt(r2 (1 - r2 / 4))
+ // cos(r) = 1 - r2 / 2
+ //
+ // We then scale both sides of (2) by |aXn| |bXn| / |n| (in order to
+ // minimize the number of calculations and to avoid divisions), which gives:
+ //
+ // cos(r) ||a| sqrt(sin^2(r) |b|^2 |n|^2 - |b.n|^2) -
+ // |b| sqrt(sin^2(r) |a|^2 |n|^2 - |a.n|^2)| > (aXb).n
+ //
+ // Furthermore we can substitute |a| = |b| = 1 (as long as this is taken
+ // into account in the error bounds), yielding
+ //
+ // (3) cos(r) |sqrt(sin^2(r) |n|^2 - |b.n|^2) -
+ // sqrt(sin^2(r) |n|^2 - |a.n|^2)| > (aXb).n
+ //
+ // The code below is more complicated than this because many expressions
+ // have been modified for better numerical stability. For example, dot
+ // products between unit vectors are computed using (x - y).DotProd(x + y),
+ // and the dot product between a point P and the normal N of an edge X is
+ // measured using (P - Xi).DotProd(N) where Xi is the endpoint of X that is
+ // closer to P.
+
+ Vector3<T> n = (x0 - x1).CrossProd(x0 + x1); // 2 * x0.CrossProd(x1)
+ T n2 = n.Norm2();
+ T n1 = sqrt(n2);
+ // This factor is used in the error terms of dot products with "n" below.
+ T Dn_error = ((3.5 + 2 * sqrt(3.0)) * n1 + 32 * sqrt(3.0) * DBL_ERR) * T_ERR;
+
+ T cos_r = 1 - 0.5 * r2;
+ T sin2_r = r2 * (1 - 0.25 * r2);
+ T n2sin2_r = n2 * sin2_r;
+
+ // "ra" and "rb" denote sin(ra) and sin(rb) after the scaling above.
+ T ax2, aDn = (a - GetClosestVertex(a, x0, x1, &ax2)).DotProd(n);
+ T aDn2 = aDn * aDn;
+ T aDn_error = Dn_error * sqrt(ax2);
+ T ra2 = n2sin2_r - aDn2;
+ T ra2_error = (8 * DBL_ERR + 4 * T_ERR) * aDn2 +
+ (2 * fabs(aDn) + aDn_error) * aDn_error + 6 * T_ERR * n2sin2_r;
+ // This is the minimum possible value of ra2, which is used to bound the
+ // derivative of sqrt(ra2) in computing ra_error below.
+ T min_ra2 = ra2 - ra2_error;
+ if (min_ra2 < 0) return Excluded::UNCERTAIN;
+ T ra = sqrt(ra2);
+ // Includes the ra2 subtraction error above.
+ T ra_error = 1.5 * T_ERR * ra + 0.5 * ra2_error / sqrt(min_ra2);
+
+ T bx2, bDn = (b - GetClosestVertex(b, x0, x1, &bx2)).DotProd(n);
+ T bDn2 = bDn * bDn;
+ T bDn_error = Dn_error * sqrt(bx2);
+ T rb2 = n2sin2_r - bDn2;
+ T rb2_error = (8 * DBL_ERR + 4 * T_ERR) * bDn2 +
+ (2 * fabs(bDn) + bDn_error) * bDn_error + 6 * T_ERR * n2sin2_r;
+ T min_rb2 = rb2 - rb2_error;
+ if (min_rb2 < 0) return Excluded::UNCERTAIN;
+ T rb = sqrt(rb2);
+ // Includes the rb2 subtraction error above.
+ T rb_error = 1.5 * T_ERR * rb + 0.5 * rb2_error / sqrt(min_rb2);
+
+ // The sign of LHS(3) determines which site may be excluded by the other.
+ T lhs3 = cos_r * (rb - ra);
+ T abs_lhs3 = fabs(lhs3);
+ T lhs3_error = cos_r * (ra_error + rb_error) + 3 * T_ERR * abs_lhs3;
+
+ // Now we evaluate the RHS of (3), which is proportional to sin(d).
+ Vector3<T> aXb = (a - b).CrossProd(a + b); // 2 * a.CrossProd(b)
+ T aXb1 = aXb.Norm();
+ T sin_d = 0.5 * aXb.DotProd(n);
+ T sin_d_error = (4 * DBL_ERR + (2.5 + 2 * sqrt(3.0)) * T_ERR) * aXb1 * n1 +
+ 16 * sqrt(3.0) * DBL_ERR * T_ERR * (aXb1 + n1);
+
+ // If LHS(3) is definitely less than RHS(3), neither site excludes the other.
+ T result = abs_lhs3 - sin_d;
+ T result_error = lhs3_error + sin_d_error;
+ if (result < -result_error) return Excluded::NEITHER;
+
+ // Otherwise, before proceeding further we need to check that |d| <= Pi/2.
+ // In fact, |d| < Pi/2 is enough because of the requirement that r < Pi/2.
+ // The following expression represents cos(d) after scaling; it is
+ // equivalent to (aXn).(bXn) but has about 30% less error.
+ T cos_d = a.DotProd(b) * n2 - aDn * bDn;
+ T cos_d_error =
+ ((8 * DBL_ERR + 5 * T_ERR) * fabs(aDn) + aDn_error) * fabs(bDn) +
+ (fabs(aDn) + aDn_error) * bDn_error + (8 * DBL_ERR + 8 * T_ERR) * n2;
+ if (cos_d <= -cos_d_error) return Excluded::NEITHER;
+
+ // Potential optimization: if the sign of cos(d) is uncertain, then instead
+ // we could check whether cos(d) >= cos(r). Unfortunately this is fairly
+ // expensive since it requires computing denominator |aXn||bXn| of cos(d)
+ // and the associated error bounds. In any case this case is relatively
+ // rare so it seems better to punt.
+ if (cos_d < cos_d_error) return Excluded::UNCERTAIN;
+
+ // Normally we have d > 0 because the sites are sorted so that A is closer
+ // to X0 and B is closer to X1. However if the edge X is longer than Pi/2,
+ // and the sites A and B are beyond its endpoints, then AB can wrap around
+ // the sphere in the opposite direction from X. In this situation d < 0 but
+ // each site is closest to one endpoint of X, so neither excludes the other.
+ //
+ // It turns out that this can happen only when the site that is further away
+ // from edge X is less than 90 degrees away from whichever endpoint of X it
+ // is closer to. It is provable that if this distance is less than 90
+ // degrees, then it is also less than r2, and therefore the Voronoi regions
+ // of both sites intersect the edge.
+ if (sin_d < -sin_d_error) {
+ T r90 = S1ChordAngle::Right().length2();
+ // "ca" is negative if Voronoi region A definitely intersects edge X.
+ int ca = (lhs3 < -lhs3_error) ? -1 : TriageCompareCosDistance(a, x0, r90);
+ int cb = (lhs3 > lhs3_error) ? -1 : TriageCompareCosDistance(b, x1, r90);
+ if (ca < 0 && cb < 0) return Excluded::NEITHER;
+ if (ca <= 0 && cb <= 0) return Excluded::UNCERTAIN;
+ if (abs_lhs3 <= lhs3_error) return Excluded::UNCERTAIN;
+ } else if (sin_d <= sin_d_error) {
+ return Excluded::UNCERTAIN;
+ }
+ // Now we can finish checking the results of predicate (3).
+ if (result <= result_error) return Excluded::UNCERTAIN;
+ S2_DCHECK_GT(abs_lhs3, lhs3_error);
+ return (lhs3 > 0) ? Excluded::FIRST : Excluded::SECOND;
+}
+
+Excluded ExactVoronoiSiteExclusion(const Vector3_xf& a, const Vector3_xf& b,
+ const Vector3_xf& x0, const Vector3_xf& x1,
+ const ExactFloat& r2) {
+ S2_DCHECK(!ArePointsAntipodal(x0, x1));
+
+ // Recall that one site excludes the other if
+ //
+ // (1) |sin(rb - ra)| > sin(d)
+ //
+ // and that the sign of (rb - ra) determines which site is excluded (see the
+ // comments in TriageVoronoiSiteExclusion). To evaluate this using exact
+ // arithmetic, we expand this to the same predicate as before:
+ //
+ // (2) cos(r) ||a| sqrt(sin^2(r) |b|^2 |n|^2 - |b.n|^2) -
+ // |b| sqrt(sin^2(r) |a|^2 |n|^2 - |a.n|^2)| > (aXb).n
+ //
+ // We also need to verify that d <= Pi/2, which is implemented by checking
+ // that sin(d) >= 0 and cos(d) >= 0.
+ //
+ // To eliminate the square roots we use the standard technique of
+ // rearranging the inequality to isolate at least one square root and then
+ // squaring both sides. We need to repeat this process twice in order to
+ // eliminate all the square roots, which leads to a polynomial predicate of
+ // degree 20 in the input arguments (i.e., degree 4 in each of "a", "b",
+ // "x0", "x1", and "r2").
+ //
+ // Before squaring we need to check the sign of each side. We also check
+ // the condition that cos(d) >= 0. Given what else we need to compute, it
+ // is cheaper use the identity (aXn).(bXn) = (a.b) |n|^2 - (a.n)(b.n) .
+ Vector3_xf n = x0.CrossProd(x1);
+ ExactFloat n2 = n.Norm2();
+ ExactFloat aDn = a.DotProd(n);
+ ExactFloat bDn = b.DotProd(n);
+ ExactFloat cos_d = a.DotProd(b) * n2 - aDn * bDn;
+ if (cos_d.sgn() < 0) return Excluded::NEITHER;
+
+ // Otherwise we continue evaluating the LHS of (2), defining
+ // sa = |b| sqrt(sin^2(r) |a|^2 |n|^2 - |a.n|^2)
+ // sb = |a| sqrt(sin^2(r) |b|^2 |n|^2 - |b.n|^2) .
+ // The sign of the LHS of (2) (before taking the absolute value) determines
+ // which coverage interval is larger and therefore which site is potentially
+ // being excluded.
+ ExactFloat a2 = a.Norm2();
+ ExactFloat b2 = b.Norm2();
+ ExactFloat n2sin2_r = r2 * (1 - 0.25 * r2) * n2;
+ ExactFloat sa2 = b2 * (n2sin2_r * a2 - aDn * aDn);
+ ExactFloat sb2 = a2 * (n2sin2_r * b2 - bDn * bDn);
+ int lhs2_sgn = (sb2 - sa2).sgn();
+
+ // If the RHS of (2) is negative (corresponding to sin(d) < 0), then we need
+ // to consider the possibility that the edge AB wraps around the sphere in
+ // the opposite direction from edge X, with the result that neither site
+ // excludes the other (see TriageVoronoiSiteExclusion).
+ ExactFloat rhs2 = a.CrossProd(b).DotProd(n);
+ int rhs2_sgn = rhs2.sgn();
+ if (rhs2_sgn < 0) {
+ ExactFloat r90 = S1ChordAngle::Right().length2();
+ int ca = (lhs2_sgn < 0) ? -1 : ExactCompareDistance(a, x0, r90);
+ int cb = (lhs2_sgn > 0) ? -1 : ExactCompareDistance(b, x1, r90);
+ if (ca <= 0 && cb <= 0) return Excluded::NEITHER;
+ S2_DCHECK(ca != 1 || cb != 1);
+ return ca == 1 ? Excluded::FIRST : Excluded::SECOND;
+ }
+ if (lhs2_sgn == 0) {
+ // If the RHS of (2) is zero as well (i.e., d == 0) then both sites are
+ // equidistant from every point on edge X. This case requires symbolic
+ // perturbations, but it should already have been handled in
+ // GetVoronoiSiteExclusion() (see the call to CompareDistances).
+ S2_DCHECK_GT(rhs2_sgn, 0);
+ return Excluded::NEITHER;
+ }
+ // Next we square both sides of (2), yielding
+ //
+ // cos^2(r) (sb^2 + sa^2 - 2 sa sb) > (aXb.n)^2
+ //
+ // which can be rearranged to give
+ //
+ // (3) cos^2(r) (sb^2 + sa^2) - (aXb.n)^2 > 2 cos^2(r) sa sb .
+ //
+ // The RHS of (3) is always non-negative, but we still need to check the
+ // sign of the LHS.
+ ExactFloat cos_r = 1 - 0.5 * r2;
+ ExactFloat cos2_r = cos_r * cos_r;
+ ExactFloat lhs3 = cos2_r * (sa2 + sb2) - rhs2 * rhs2;
+ if (lhs3.sgn() < 0) return Excluded::NEITHER;
+
+ // Otherwise we square both sides of (3) to obtain:
+ //
+ // (4) LHS(3)^2 > 4 cos^4(r) sa^2 sb^2
+ ExactFloat lhs4 = lhs3 * lhs3;
+ ExactFloat rhs4 = 4 * cos2_r * cos2_r * sa2 * sb2;
+ int result = (lhs4 - rhs4).sgn();
+ if (result < 0) return Excluded::NEITHER;
+ if (result == 0) {
+ // We have |rb - ra| = d and d > 0. This implies that one coverage
+ // interval contains the other, but not properly: the two intervals share
+ // a common endpoint. The distance from each site to that point is
+ // exactly "r", therefore we need to use symbolic perturbations. Recall
+ // that site A is considered closer to an equidistant point if and only if
+ // A > B. Therefore if (rb > ra && A > B) or (ra > rb && B > A) then each
+ // site is closer to at least one point and neither site is excluded.
+ //
+ // Ideally this logic would be in a separate SymbolicVoronoiSiteExclusion
+ // method for better testing, but this is not convenient because it needs
+ // lhs_sgn (which requires exact arithmetic to compute).
+ if ((lhs2_sgn > 0) == (a > b)) return Excluded::NEITHER;
+ }
+ // At this point we know that one of the two sites is excluded. The sign of
+ // the LHS of (2) (before the absolute value) determines which one.
+ return (lhs2_sgn > 0) ? Excluded::FIRST : Excluded::SECOND;
+}
+
+Excluded GetVoronoiSiteExclusion(const S2Point& a, const S2Point& b,
+ const S2Point& x0, const S2Point& x1,
+ S1ChordAngle r) {
+ S2_DCHECK_LT(r, S1ChordAngle::Right());
+ S2_DCHECK_LT(s2pred::CompareDistances(x0, a, b), 0); // (implies a != b)
+ S2_DCHECK_LE(s2pred::CompareEdgeDistance(a, x0, x1, r), 0);
+ S2_DCHECK_LE(s2pred::CompareEdgeDistance(b, x0, x1, r), 0);
+ // Check that the edge does not consist of antipodal points. (This catches
+ // the most common case -- the full test is in ExactVoronoiSiteExclusion.)
+ S2_DCHECK_NE(x0, -x1);
+
+ // If one site is closer than the other to both endpoints of X, then it is
+ // closer to every point on X. Note that this also handles the case where A
+ // and B are equidistant from every point on X (i.e., X is the perpendicular
+ // bisector of AB), because CompareDistances uses symbolic perturbations to
+ // ensure that either A or B is considered closer (in a consistent way).
+ // This also ensures that the choice of A or B does not depend on the
+ // direction of X.
+ if (s2pred::CompareDistances(x1, a, b) < 0) {
+ return Excluded::SECOND; // Site A is closer to every point on X.
+ }
+
+ Excluded result = TriageVoronoiSiteExclusion(a, b, x0, x1, r.length2());
+ if (result != Excluded::UNCERTAIN) return result;
+
+ result = TriageVoronoiSiteExclusion(ToLD(a), ToLD(b), ToLD(x0), ToLD(x1),
+ ToLD(r.length2()));
+ if (result != Excluded::UNCERTAIN) return result;
+
+ return ExactVoronoiSiteExclusion(ToExact(a), ToExact(b), ToExact(x0),
+ ToExact(x1), r.length2());
+}
+
+std::ostream& operator<<(std::ostream& os, Excluded excluded) {
+ switch (excluded) {
+ case Excluded::FIRST: return os << "FIRST";
+ case Excluded::SECOND: return os << "SECOND";
+ case Excluded::NEITHER: return os << "NEITHER";
+ case Excluded::UNCERTAIN: return os << "UNCERTAIN";
+ default: return os << "Unknown enum value";
+ }
+}
+
+// Explicitly instantiate all of the template functions above so that the
+// tests can use them without putting all the definitions in a header file.
+
+template int TriageCompareCosDistances<double>(
+ const S2Point&, const S2Point&, const S2Point&);
+
+template int TriageCompareCosDistances<long double>(
+ const Vector3_ld&, const Vector3_ld&, const Vector3_ld&);
+
+template int TriageCompareSin2Distances<double>(
+ const S2Point&, const S2Point&, const S2Point&);
+
+template int TriageCompareSin2Distances<long double>(
+ const Vector3_ld&, const Vector3_ld&, const Vector3_ld&);
+
+template int TriageCompareCosDistance<double>(
+ const S2Point&, const S2Point&, double r2);
+
+template int TriageCompareCosDistance<long double>(
+ const Vector3_ld&, const Vector3_ld&, long double r2);
+
+template int TriageCompareSin2Distance<double>(
+ const S2Point&, const S2Point&, double r2);
+
+template int TriageCompareSin2Distance<long double>(
+ const Vector3_ld&, const Vector3_ld&, long double r2);
+
+template int TriageCompareEdgeDistance<double>(
+ const S2Point& x, const S2Point& a0, const S2Point& a1, double r2);
+
+template int TriageCompareEdgeDistance<long double>(
+ const Vector3_ld& x, const Vector3_ld& a0, const Vector3_ld& a1,
+ long double r2);
+
+template int TriageCompareEdgeDirections<double>(
+ const S2Point& a0, const S2Point& a1,
+ const S2Point& b0, const S2Point& b1);
+
+template int TriageCompareEdgeDirections<long double>(
+ const Vector3_ld& a0, const Vector3_ld& a1,
+ const Vector3_ld& b0, const Vector3_ld& b1);
+
+template int TriageEdgeCircumcenterSign<double>(
+ const S2Point& x0, const S2Point& x1,
+ const S2Point& a, const S2Point& b, const S2Point& c, int abc_sign);
+
+template int TriageEdgeCircumcenterSign<long double>(
+ const Vector3_ld& x0, const Vector3_ld& x1,
+ const Vector3_ld& a, const Vector3_ld& b, const Vector3_ld& c,
+ int abc_sign);
+
+template Excluded TriageVoronoiSiteExclusion(
+ const S2Point& a, const S2Point& b,
+ const S2Point& x0, const S2Point& x1, double r2);
+
+template Excluded TriageVoronoiSiteExclusion(
+ const Vector3_ld& a, const Vector3_ld& b,
+ const Vector3_ld& x0, const Vector3_ld& x1, long double r2);
+
+} // namespace s2pred
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2projections.h"
+
+#include <cmath>
+#include "s2/s2latlng.h"
+
+using std::fabs;
+
+namespace S2 {
+
+R2Point Projection::WrapDestination(const R2Point& a, const R2Point& b) const {
+ R2Point wrap = wrap_distance();
+ double x = b.x(), y = b.y();
+ // The code below ensures that "b" is unmodified unless wrapping is required.
+ if (wrap.x() > 0 && fabs(x - a.x()) > 0.5 * wrap.x()) {
+ x = a.x() + remainder(x - a.x(), wrap.x());
+ }
+ if (wrap.y() > 0 && fabs(y - a.y()) > 0.5 * wrap.y()) {
+ y = a.y() + remainder(y - a.y(), wrap.y());
+ }
+ return R2Point(x, y);
+}
+
+// Default implementation, suitable for any projection where edges are defined
+// as straight lines in the 2D projected space.
+R2Point Projection::Interpolate(double f,
+ const R2Point& a, const R2Point& b) const {
+ return (1 - f) * a + f * b;
+}
+
+PlateCarreeProjection::PlateCarreeProjection(double x_scale)
+ : x_wrap_(2 * x_scale),
+ to_radians_(M_PI / x_scale),
+ from_radians_(x_scale / M_PI) {
+}
+
+R2Point PlateCarreeProjection::Project(const S2Point& p) const {
+ return FromLatLng(S2LatLng(p));
+}
+
+R2Point PlateCarreeProjection::FromLatLng(const S2LatLng& ll) const {
+ return R2Point(from_radians_ * ll.lng().radians(),
+ from_radians_ * ll.lat().radians());
+}
+
+S2Point PlateCarreeProjection::Unproject(const R2Point& p) const {
+ return ToLatLng(p).ToPoint();
+}
+
+S2LatLng PlateCarreeProjection::ToLatLng(const R2Point& p) const {
+ return S2LatLng::FromRadians(to_radians_ * p.y(),
+ to_radians_ * remainder(p.x(), x_wrap_));
+}
+
+R2Point PlateCarreeProjection::wrap_distance() const {
+ return R2Point(x_wrap_, 0);
+}
+
+MercatorProjection::MercatorProjection(double max_x)
+ : x_wrap_(2 * max_x),
+ to_radians_(M_PI / max_x),
+ from_radians_(max_x / M_PI) {
+}
+
+R2Point MercatorProjection::Project(const S2Point& p) const {
+ return FromLatLng(S2LatLng(p));
+}
+
+R2Point MercatorProjection::FromLatLng(const S2LatLng& ll) const {
+ // This formula is more accurate near zero than the log(tan()) version.
+ // Note that latitudes of +/- 90 degrees yield "y" values of +/- infinity.
+ double sin_phi = sin(ll.lat());
+ double y = 0.5 * log((1 + sin_phi) / (1 - sin_phi));
+ return R2Point(from_radians_ * ll.lng().radians(), from_radians_ * y);
+}
+
+S2Point MercatorProjection::Unproject(const R2Point& p) const {
+ return ToLatLng(p).ToPoint();
+}
+
+S2LatLng MercatorProjection::ToLatLng(const R2Point& p) const {
+ // This formula is more accurate near zero than the atan(exp()) version.
+ double x = to_radians_ * remainder(p.x(), x_wrap_);
+ double k = exp(2 * to_radians_ * p.y());
+ double y = std::isinf(k) ? M_PI_2 : asin((k - 1) / (k + 1));
+ return S2LatLng::FromRadians(y, x);
+}
+
+R2Point MercatorProjection::wrap_distance() const {
+ return R2Point(x_wrap_, 0);
+}
+
+} // namespace S2
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2r2rect.h"
+
+#include <iosfwd>
+
+#include "s2/base/logging.h"
+#include "s2/r1interval.h"
+#include "s2/s2cap.h"
+#include "s2/s2cell.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2coords.h"
+#include "s2/s2latlng_rect.h"
+
+S2R2Rect S2R2Rect::FromCell(const S2Cell& cell) {
+ // S2Cells have a more efficient GetSizeST() method than S2CellIds.
+ double size = cell.GetSizeST();
+ return FromCenterSize(cell.id().GetCenterST(), R2Point(size, size));
+}
+
+S2R2Rect S2R2Rect::FromCellId(S2CellId id) {
+ double size = id.GetSizeST();
+ return FromCenterSize(id.GetCenterST(), R2Point(size, size));
+}
+
+S2R2Rect* S2R2Rect::Clone() const {
+ return new S2R2Rect(*this);
+}
+
+S2Point S2R2Rect::ToS2Point(const R2Point& p) {
+ return S2::FaceUVtoXYZ(0, S2::STtoUV(p.x()), S2::STtoUV(p.y())).Normalize();
+}
+
+S2Cap S2R2Rect::GetCapBound() const {
+ if (is_empty()) return S2Cap::Empty();
+
+ // The rectangle is a convex polygon on the sphere, since it is a subset of
+ // one cube face. Its bounding cap is also a convex region on the sphere,
+ // and therefore we can bound the rectangle by just bounding its vertices.
+ // We use the rectangle's center in (s,t)-space as the cap axis. This
+ // doesn't yield the minimal cap but it's pretty close.
+ S2Cap cap = S2Cap::FromPoint(ToS2Point(GetCenter()));
+ for (int k = 0; k < 4; ++k) {
+ cap.AddPoint(ToS2Point(GetVertex(k)));
+ }
+ return cap;
+}
+
+S2LatLngRect S2R2Rect::GetRectBound() const {
+ // This is not very tight but hopefully good enough.
+ return GetCapBound().GetRectBound();
+}
+
+bool S2R2Rect::Contains(const S2Point& p) const {
+ if (S2::GetFace(p) != 0) return false;
+ double u, v;
+ S2::ValidFaceXYZtoUV(0, p, &u, &v);
+ return Contains(R2Point(S2::UVtoST(u), S2::UVtoST(v)));
+}
+
+bool S2R2Rect::Contains(const S2Cell& cell) const {
+ if (cell.face() != 0) return false;
+ return Contains(S2R2Rect::FromCell(cell));
+}
+
+bool S2R2Rect::MayIntersect(const S2Cell& cell) const {
+ if (cell.face() != 0) return false;
+ return Intersects(S2R2Rect::FromCell(cell));
+}
+
+std::ostream& operator<<(std::ostream& os, const S2R2Rect& r) {
+ return os << "[Lo" << r.lo() << ", Hi" << r.hi() << "]";
+}
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2region.h"
+
+#include <vector>
+
+#include "s2/s2cap.h"
+
+void S2Region::GetCellUnionBound(std::vector<S2CellId> *cell_ids) const {
+ return GetCapBound().GetCellUnionBound(cell_ids);
+}
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2region_coverer.h"
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdlib>
+#include <cstring>
+#include <functional>
+#include <queue>
+#include <unordered_set>
+#include <vector>
+
+#include "s2/base/logging.h"
+#include "s2/s1angle.h"
+#include "s2/s2cap.h"
+#include "s2/s2cell_union.h"
+#include "s2/s2metrics.h"
+#include "s2/s2region.h"
+#include "s2/third_party/absl/base/casts.h"
+
+using std::is_sorted;
+using std::max;
+using std::min;
+using std::unordered_set;
+using std::vector;
+
+// Define storage for header file constants (the values are not needed here).
+constexpr int S2RegionCoverer::Options::kDefaultMaxCells;
+
+S2RegionCoverer::S2RegionCoverer(const S2RegionCoverer::Options& options) :
+ options_(options) {
+ S2_DCHECK_LE(options.min_level(), options.max_level());
+}
+
+// Defaulted in the implementation to prevent inline bloat.
+S2RegionCoverer::S2RegionCoverer() = default;
+S2RegionCoverer::~S2RegionCoverer() = default;
+S2RegionCoverer::S2RegionCoverer(S2RegionCoverer&&) = default;
+S2RegionCoverer& S2RegionCoverer::operator=(S2RegionCoverer&&) = default;
+
+void S2RegionCoverer::Options::set_max_cells(int max_cells) {
+ max_cells_ = max_cells;
+}
+
+void S2RegionCoverer::Options::set_min_level(int min_level) {
+ S2_DCHECK_GE(min_level, 0);
+ S2_DCHECK_LE(min_level, S2CellId::kMaxLevel);
+ // min_level() <= max_level() is checked by S2RegionCoverer.
+ min_level_ = max(0, min(S2CellId::kMaxLevel, min_level));
+}
+
+void S2RegionCoverer::Options::set_max_level(int max_level) {
+ S2_DCHECK_GE(max_level, 0);
+ S2_DCHECK_LE(max_level, S2CellId::kMaxLevel);
+ // min_level() <= max_level() is checked by S2RegionCoverer.
+ max_level_ = max(0, min(S2CellId::kMaxLevel, max_level));
+}
+
+void S2RegionCoverer::Options::set_fixed_level(int level) {
+ set_min_level(level);
+ set_max_level(level);
+}
+
+void S2RegionCoverer::Options::set_level_mod(int level_mod) {
+ S2_DCHECK_GE(level_mod, 1);
+ S2_DCHECK_LE(level_mod, 3);
+ level_mod_ = max(1, min(3, level_mod));
+}
+
+int S2RegionCoverer::Options::true_max_level() const {
+ if (level_mod_ == 1) return max_level_;
+ return max_level_ - (max_level_ - min_level_) % level_mod_;
+}
+
+S2RegionCoverer::Candidate* S2RegionCoverer::NewCandidate(const S2Cell& cell) {
+ if (!region_->MayIntersect(cell)) return nullptr;
+
+ bool is_terminal = false;
+ if (cell.level() >= options_.min_level()) {
+ if (interior_covering_) {
+ if (region_->Contains(cell)) {
+ is_terminal = true;
+ } else if (cell.level() + options_.level_mod() > options_.max_level()) {
+ return nullptr;
+ }
+ } else {
+ if (cell.level() + options_.level_mod() > options_.max_level() ||
+ region_->Contains(cell)) {
+ is_terminal = true;
+ }
+ }
+ }
+ ++candidates_created_counter_;
+ const std::size_t max_children = is_terminal ? 0 : 1 << max_children_shift();
+ return new (max_children) Candidate(cell, max_children);
+}
+
+void S2RegionCoverer::DeleteCandidate(Candidate* candidate,
+ bool delete_children) {
+ if (delete_children) {
+ for (int i = 0; i < candidate->num_children; ++i)
+ DeleteCandidate(candidate->children[i], true);
+ }
+ delete candidate;
+}
+
+int S2RegionCoverer::ExpandChildren(Candidate* candidate,
+ const S2Cell& cell, int num_levels) {
+ num_levels--;
+ S2Cell child_cells[4];
+ cell.Subdivide(child_cells);
+ int num_terminals = 0;
+ for (int i = 0; i < 4; ++i) {
+ if (num_levels > 0) {
+ if (region_->MayIntersect(child_cells[i])) {
+ num_terminals += ExpandChildren(candidate, child_cells[i], num_levels);
+ }
+ continue;
+ }
+ Candidate* child = NewCandidate(child_cells[i]);
+ if (child) {
+ candidate->children[candidate->num_children++] = child;
+ if (child->is_terminal) ++num_terminals;
+ }
+ }
+ return num_terminals;
+}
+
+void S2RegionCoverer::AddCandidate(Candidate* candidate) {
+ if (candidate == nullptr) return;
+
+ if (candidate->is_terminal) {
+ result_.push_back(candidate->cell.id());
+ DeleteCandidate(candidate, true);
+ return;
+ }
+ S2_DCHECK_EQ(0, candidate->num_children);
+
+ // Expand one level at a time until we hit min_level() to ensure that we
+ // don't skip over it.
+ int num_levels = ((candidate->cell.level() < options_.min_level()) ?
+ 1 : options_.level_mod());
+ int num_terminals = ExpandChildren(candidate, candidate->cell, num_levels);
+
+ if (candidate->num_children == 0) {
+ DeleteCandidate(candidate, false);
+
+ } else if (!interior_covering_ &&
+ num_terminals == 1 << max_children_shift() &&
+ candidate->cell.level() >= options_.min_level()) {
+ // Optimization: add the parent cell rather than all of its children.
+ // We can't do this for interior coverings, since the children just
+ // intersect the region, but may not be contained by it - we need to
+ // subdivide them further.
+ candidate->is_terminal = true;
+ AddCandidate(candidate);
+
+ } else {
+ // We negate the priority so that smaller absolute priorities are returned
+ // first. The heuristic is designed to refine the largest cells first,
+ // since those are where we have the largest potential gain. Among cells
+ // of the same size, we prefer the cells with the fewest children.
+ // Finally, among cells with equal numbers of children we prefer those
+ // with the smallest number of children that cannot be refined further.
+ int priority = -((((candidate->cell.level() << max_children_shift())
+ + candidate->num_children) << max_children_shift())
+ + num_terminals);
+ pq_.push(std::make_pair(priority, candidate));
+ S2_VLOG(2) << "Push: " << candidate->cell.id() << " (" << priority << ") ";
+ }
+}
+
+inline int S2RegionCoverer::AdjustLevel(int level) const {
+ if (options_.level_mod() > 1 && level > options_.min_level()) {
+ level -= (level - options_.min_level()) % options_.level_mod();
+ }
+ return level;
+}
+
+void S2RegionCoverer::AdjustCellLevels(vector<S2CellId>* cells) const {
+ S2_DCHECK(is_sorted(cells->begin(), cells->end()));
+ if (options_.level_mod() == 1) return;
+
+ int out = 0;
+ for (S2CellId id : *cells) {
+ int level = id.level();
+ int new_level = AdjustLevel(level);
+ if (new_level != level) id = id.parent(new_level);
+ if (out > 0 && (*cells)[out-1].contains(id)) continue;
+ while (out > 0 && id.contains((*cells)[out-1])) --out;
+ (*cells)[out++] = id;
+ }
+ cells->resize(out);
+}
+
+void S2RegionCoverer::GetInitialCandidates() {
+ // Optimization: start with a small (usually 4 cell) covering of the
+ // region's bounding cap.
+ S2RegionCoverer tmp_coverer;
+ tmp_coverer.mutable_options()->set_max_cells(min(4, options_.max_cells()));
+ tmp_coverer.mutable_options()->set_max_level(options_.max_level());
+ vector<S2CellId> cells;
+ tmp_coverer.GetFastCovering(*region_, &cells);
+ AdjustCellLevels(&cells);
+ for (S2CellId cell_id : cells) {
+ AddCandidate(NewCandidate(S2Cell(cell_id)));
+ }
+}
+
+void S2RegionCoverer::GetCoveringInternal(const S2Region& region) {
+ // We check this on each call because of mutable_options().
+ S2_DCHECK_LE(options_.min_level(), options_.max_level());
+
+ // Strategy: Start with the 6 faces of the cube. Discard any
+ // that do not intersect the shape. Then repeatedly choose the
+ // largest cell that intersects the shape and subdivide it.
+ //
+ // result_ contains the cells that will be part of the output, while pq_
+ // contains cells that we may still subdivide further. Cells that are
+ // entirely contained within the region are immediately added to the output,
+ // while cells that do not intersect the region are immediately discarded.
+ // Therefore pq_ only contains cells that partially intersect the region.
+ // Candidates are prioritized first according to cell size (larger cells
+ // first), then by the number of intersecting children they have (fewest
+ // children first), and then by the number of fully contained children
+ // (fewest children first).
+
+ S2_DCHECK(pq_.empty());
+ S2_DCHECK(result_.empty());
+ region_ = ®ion;
+ candidates_created_counter_ = 0;
+
+ GetInitialCandidates();
+ while (!pq_.empty() &&
+ (!interior_covering_ || result_.size() < options_.max_cells())) {
+ Candidate* candidate = pq_.top().second;
+ pq_.pop();
+ S2_VLOG(2) << "Pop: " << candidate->cell.id();
+ // For interior coverings we keep subdividing no matter how many children
+ // the candidate has. If we reach max_cells() before expanding all
+ // children, we will just use some of them. For exterior coverings we
+ // cannot do this, because the result has to cover the whole region, so
+ // all children have to be used. The (candidate->num_children == 1) case
+ // takes care of the situation when we already have more than max_cells()
+ // in results (min_level is too high). Subdividing the candidate with one
+ // child does no harm in this case.
+ if (interior_covering_ ||
+ candidate->cell.level() < options_.min_level() ||
+ candidate->num_children == 1 ||
+ (result_.size() + pq_.size() + candidate->num_children <=
+ options_.max_cells())) {
+ // Expand this candidate into its children.
+ for (int i = 0; i < candidate->num_children; ++i) {
+ if (interior_covering_ && result_.size() >= options_.max_cells()) {
+ DeleteCandidate(candidate->children[i], true);
+ } else {
+ AddCandidate(candidate->children[i]);
+ }
+ }
+ DeleteCandidate(candidate, false);
+ } else {
+ candidate->is_terminal = true;
+ AddCandidate(candidate);
+ }
+ }
+ S2_VLOG(2) << "Created " << result_.size() << " cells, " <<
+ candidates_created_counter_ << " candidates created, " <<
+ pq_.size() << " left";
+ while (!pq_.empty()) {
+ DeleteCandidate(pq_.top().second, true);
+ pq_.pop();
+ }
+ region_ = nullptr;
+
+ // Rather than just returning the raw list of cell ids, we construct a cell
+ // union and then denormalize it. This has the effect of replacing four
+ // child cells with their parent whenever this does not violate the covering
+ // parameters specified (min_level, level_mod, etc). This significantly
+ // reduces the number of cells returned in many cases, and it is cheap
+ // compared to computing the covering in the first place.
+ S2CellUnion::Normalize(&result_);
+ if (options_.min_level() > 0 || options_.level_mod() > 1) {
+ auto result_copy = result_;
+ S2CellUnion::Denormalize(result_copy, options_.min_level(),
+ options_.level_mod(), &result_);
+ }
+ S2_DCHECK(IsCanonical(result_));
+}
+
+void S2RegionCoverer::GetCovering(const S2Region& region,
+ vector<S2CellId>* covering) {
+ interior_covering_ = false;
+ GetCoveringInternal(region);
+ *covering = std::move(result_);
+}
+
+void S2RegionCoverer::GetInteriorCovering(const S2Region& region,
+ vector<S2CellId>* interior) {
+ interior_covering_ = true;
+ GetCoveringInternal(region);
+ *interior = std::move(result_);
+}
+
+S2CellUnion S2RegionCoverer::GetCovering(const S2Region& region) {
+ interior_covering_ = false;
+ GetCoveringInternal(region);
+ return S2CellUnion::FromVerbatim(std::move(result_));
+}
+
+S2CellUnion S2RegionCoverer::GetInteriorCovering(const S2Region& region) {
+ interior_covering_ = true;
+ GetCoveringInternal(region);
+ return S2CellUnion::FromVerbatim(std::move(result_));
+}
+
+void S2RegionCoverer::GetFastCovering(const S2Region& region,
+ vector<S2CellId>* covering) {
+ region.GetCellUnionBound(covering);
+ CanonicalizeCovering(covering);
+}
+
+bool S2RegionCoverer::IsCanonical(const S2CellUnion& covering) const {
+ return IsCanonical(covering.cell_ids());
+}
+
+bool S2RegionCoverer::IsCanonical(const vector<S2CellId>& covering) const {
+ // We check this on each call because of mutable_options().
+ S2_DCHECK_LE(options_.min_level(), options_.max_level());
+
+ const int min_level = options_.min_level();
+ const int max_level = options_.true_max_level();
+ const int level_mod = options_.level_mod();
+ const bool too_many_cells = covering.size() > options_.max_cells();
+ int same_parent_count = 1;
+ S2CellId prev_id = S2CellId::None();
+ for (const S2CellId id : covering) {
+ if (!id.is_valid()) return false;
+
+ // Check that the S2CellId level is acceptable.
+ const int level = id.level();
+ if (level < min_level || level > max_level) return false;
+ if (level_mod > 1 && (level - min_level) % level_mod != 0) return false;
+
+ if (prev_id != S2CellId::None()) {
+ // Check that cells are sorted and non-overlapping.
+ if (prev_id.range_max() >= id.range_min()) return false;
+
+ // If there are too many cells, check that no pair of adjacent cells
+ // could be replaced by an ancestor.
+ if (too_many_cells && id.GetCommonAncestorLevel(prev_id) >= min_level) {
+ return false;
+ }
+
+ // Check that there are no sequences of (4 ** level_mod) cells that all
+ // have the same parent (considering only multiples of "level_mod").
+ int plevel = level - level_mod;
+ if (plevel < min_level || level != prev_id.level() ||
+ id.parent(plevel) != prev_id.parent(plevel)) {
+ same_parent_count = 1;
+ } else if (++same_parent_count == (1 << (2 * level_mod))) {
+ return false;
+ }
+ }
+ prev_id = id;
+ }
+ return true;
+}
+
+bool S2RegionCoverer::ContainsAllChildren(const vector<S2CellId>& covering,
+ S2CellId id) const {
+ auto it = std::lower_bound(covering.begin(), covering.end(), id.range_min());
+ int level = id.level() + options_.level_mod();
+ for (S2CellId child = id.child_begin(level);
+ child != id.child_end(level); ++it, child = child.next()) {
+ if (it == covering.end() || *it != child) return false;
+ }
+ return true;
+}
+
+// Replaces all descendants of "id" in "covering" with "id".
+// REQUIRES: "covering" contains at least one descendant of "id".
+void S2RegionCoverer::ReplaceCellsWithAncestor(vector<S2CellId>* covering,
+ S2CellId id) const {
+ auto begin = std::lower_bound(covering->begin(), covering->end(),
+ id.range_min());
+ auto end = std::upper_bound(covering->begin(), covering->end(),
+ id.range_max());
+ S2_DCHECK(begin != end);
+ covering->erase(begin + 1, end);
+ *begin = id;
+}
+
+S2CellUnion S2RegionCoverer::CanonicalizeCovering(const S2CellUnion& covering) {
+ vector<S2CellId> ids = covering.cell_ids();
+ CanonicalizeCovering(&ids);
+ return S2CellUnion(std::move(ids));
+}
+
+void S2RegionCoverer::CanonicalizeCovering(vector<S2CellId>* covering) {
+ // We check this on each call because of mutable_options().
+ S2_DCHECK_LE(options_.min_level(), options_.max_level());
+
+ // Note that when the covering parameters have their default values, almost
+ // all of the code in this function is skipped.
+
+ // If any cells are too small, or don't satisfy level_mod(), then replace
+ // them with ancestors.
+ if (options_.max_level() < S2CellId::kMaxLevel || options_.level_mod() > 1) {
+ for (S2CellId& id : *covering) {
+ int level = id.level();
+ int new_level = AdjustLevel(min(level, options_.max_level()));
+ if (new_level != level) {
+ id = id.parent(new_level);
+ }
+ }
+ }
+
+ // Sort the cells and simplify them.
+ S2CellUnion::Normalize(covering);
+
+ // Make sure that the covering satisfies min_level() and level_mod(),
+ // possibly at the expense of satisfying max_cells().
+ if (options_.min_level() > 0 || options_.level_mod() > 1) {
+ S2CellUnion::Denormalize(*covering, options_.min_level(),
+ options_.level_mod(), &result_);
+ *covering = std::move(result_);
+ }
+
+ // If there are too many cells and the covering is very large, use the
+ // S2RegionCoverer to compute a new covering. (This avoids possible O(n^2)
+ // behavior of the simpler algorithm below.)
+ int64 excess = covering->size() - options_.max_cells();
+ if (excess <= 0 || IsCanonical(*covering)) {
+ return;
+ }
+ if (excess * covering->size() > 10000) {
+ GetCovering(S2CellUnion(std::move(*covering)), covering);
+ } else {
+ // Repeatedly replace two adjacent cells in S2CellId order by their lowest
+ // common ancestor until the number of cells is acceptable.
+ while (covering->size() > options_.max_cells()) {
+ int best_index = -1, best_level = -1;
+ for (int i = 0; i + 1 < covering->size(); ++i) {
+ int level = (*covering)[i].GetCommonAncestorLevel((*covering)[i+1]);
+ level = AdjustLevel(level);
+ if (level > best_level) {
+ best_level = level;
+ best_index = i;
+ }
+ }
+ if (best_level < options_.min_level()) break;
+
+ // Replace all cells contained by the new ancestor cell.
+ S2CellId id = (*covering)[best_index].parent(best_level);
+ ReplaceCellsWithAncestor(covering, id);
+
+ // Now repeatedly check whether all children of the parent cell are
+ // present, in which case we can replace those cells with their parent.
+ while (best_level > options_.min_level()) {
+ best_level -= options_.level_mod();
+ id = id.parent(best_level);
+ if (!ContainsAllChildren(*covering, id)) break;
+ ReplaceCellsWithAncestor(covering, id);
+ }
+ }
+ }
+ S2_DCHECK(IsCanonical(*covering));
+}
+
+void S2RegionCoverer::FloodFill(const S2Region& region, S2CellId start,
+ vector<S2CellId>* output) {
+ unordered_set<S2CellId, S2CellIdHash> all;
+ vector<S2CellId> frontier;
+ output->clear();
+ all.insert(start);
+ frontier.push_back(start);
+ while (!frontier.empty()) {
+ S2CellId id = frontier.back();
+ frontier.pop_back();
+ if (!region.MayIntersect(S2Cell(id))) continue;
+ output->push_back(id);
+
+ S2CellId neighbors[4];
+ id.GetEdgeNeighbors(neighbors);
+ for (int edge = 0; edge < 4; ++edge) {
+ S2CellId nbr = neighbors[edge];
+ if (all.insert(nbr).second) {
+ frontier.push_back(nbr);
+ }
+ }
+ }
+}
+
+void S2RegionCoverer::GetSimpleCovering(
+ const S2Region& region, const S2Point& start,
+ int level, vector<S2CellId>* output) {
+ return FloodFill(region, S2CellId(start).parent(level), output);
+}
--- /dev/null
+// Copyright 2006 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+
+#include "s2/s2region_intersection.h"
+
+#include "s2/s2cap.h"
+#include "s2/s2latlng_rect.h"
+
+using std::vector;
+
+S2RegionIntersection::S2RegionIntersection(
+ vector<std::unique_ptr<S2Region>> regions) {
+ Init(std::move(regions));
+}
+
+void S2RegionIntersection::Init(vector<std::unique_ptr<S2Region>> regions) {
+ S2_DCHECK(regions_.empty());
+ regions_ = std::move(regions);
+}
+
+S2RegionIntersection::S2RegionIntersection(const S2RegionIntersection& src)
+ : regions_(src.num_regions()) {
+ for (int i = 0; i < num_regions(); ++i) {
+ regions_[i].reset(src.region(i)->Clone());
+ }
+}
+
+vector<std::unique_ptr<S2Region>> S2RegionIntersection::Release() {
+ vector<std::unique_ptr<S2Region>> result;
+ result.swap(regions_);
+ return result;
+}
+
+S2RegionIntersection* S2RegionIntersection::Clone() const {
+ return new S2RegionIntersection(*this);
+}
+
+S2Cap S2RegionIntersection::GetCapBound() const {
+ // TODO(ericv): This could be optimized to return a tighter bound, but
+ // doesn't seem worth it unless profiling shows otherwise.
+ return GetRectBound().GetCapBound();
+}
+
+S2LatLngRect S2RegionIntersection::GetRectBound() const {
+ S2LatLngRect result = S2LatLngRect::Full();
+ for (int i = 0; i < num_regions(); ++i) {
+ result = result.Intersection(region(i)->GetRectBound());
+ }
+ return result;
+}
+
+bool S2RegionIntersection::Contains(const S2Cell& cell) const {
+ for (int i = 0; i < num_regions(); ++i) {
+ if (!region(i)->Contains(cell)) return false;
+ }
+ return true;
+}
+
+bool S2RegionIntersection::Contains(const S2Point& p) const {
+ for (int i = 0; i < num_regions(); ++i) {
+ if (!region(i)->Contains(p)) return false;
+ }
+ return true;
+}
+
+bool S2RegionIntersection::MayIntersect(const S2Cell& cell) const {
+ for (int i = 0; i < num_regions(); ++i) {
+ if (!region(i)->MayIntersect(cell)) return false;
+ }
+ return true;
+}
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// Indexing Strategy
+// -----------------
+//
+// Given a query region, we want to find all of the document regions that
+// intersect it. The first step is to represent all the regions as S2Cell
+// coverings (see S2RegionCoverer). We then split the problem into two parts,
+// namely finding the document regions that are "smaller" than the query
+// region and those that are "larger" than the query region.
+//
+// We do this by defining two terms for each S2CellId: a "covering term" and
+// an "ancestor term". (In the implementation below, covering terms are
+// distinguished by prefixing a '$' to them.) For each document region, we
+// insert a covering term for every cell in the region's covering, and we
+// insert an ancestor term for these cells *and* all of their ancestors.
+//
+// Then given a query region, we can look up all the document regions that
+// intersect its covering by querying the union of the following terms:
+//
+// 1. An "ancestor term" for each cell in the query region. These terms
+// ensure that we find all document regions that are "smaller" than the
+// query region, i.e. where the query region contains a cell that is either
+// a cell of a document region or one of its ancestors.
+//
+// 2. A "covering term" for every ancestor of the cells in the query region.
+// These terms ensure that we find all the document regions that are
+// "larger" than the query region, i.e. where document region contains a
+// cell that is a (proper) ancestor of a cell in the query region.
+//
+// Together, these terms find all of the document regions that intersect the
+// query region. Furthermore, the number of terms to be indexed and queried
+// are both fairly small, and can be bounded in terms of max_cells() and the
+// number of cell levels used.
+//
+// Optimizations
+// -------------
+//
+// + Cells at the maximum level being indexed (max_level()) have the special
+// property that they will never be an ancestor of a cell in the query
+// region. Therefore we can safely skip generating "covering terms" for
+// these cells (see query step 2 above).
+//
+// + If the index will contain only points (rather than general regions), then
+// we can skip all the covering terms mentioned above because there will
+// never be any document regions larger than the query region. This can
+// significantly reduce the size of queries.
+//
+// + If it is more important to optimize index size rather than query speed,
+// the number of index terms can be reduced by creating ancestor terms only
+// for the *proper* ancestors of the cells in a document region, and
+// compensating for this by including covering terms for all cells in the
+// query region (in addition to their ancestors).
+//
+// Effectively, when the query region and a document region contain exactly
+// the same cell, we have a choice about whether to treat this match as a
+// "covering term" or an "ancestor term". One choice minimizes query size
+// while the other minimizes index size.
+
+#include "s2/s2region_term_indexer.h"
+
+#include <cctype>
+
+#include "s2/base/logging.h"
+#include "s2/s1angle.h"
+#include "s2/s2cap.h"
+#include "s2/s2cell_id.h"
+#include "s2/s2region.h"
+#include "s2/third_party/absl/strings/str_cat.h"
+
+using absl::string_view;
+using std::vector;
+
+S2RegionTermIndexer::Options::Options() {
+ // Override the S2RegionCoverer defaults.
+ set_max_cells(8);
+ set_min_level(4);
+ set_max_level(16);
+ set_level_mod(1);
+}
+
+void S2RegionTermIndexer::Options::set_marker_character(char ch) {
+ S2_DCHECK(!std::isalnum(ch));
+ marker_ = string(1, ch);
+}
+
+S2RegionTermIndexer::S2RegionTermIndexer(const Options& options)
+ : options_(options) {
+}
+
+// Defaulted in the implementation to prevent inline bloat.
+S2RegionTermIndexer::S2RegionTermIndexer() = default;
+S2RegionTermIndexer::~S2RegionTermIndexer() = default;
+S2RegionTermIndexer::S2RegionTermIndexer(S2RegionTermIndexer&&) = default;
+S2RegionTermIndexer& S2RegionTermIndexer::operator=(S2RegionTermIndexer&&) =
+ default;
+
+string S2RegionTermIndexer::GetTerm(TermType term_type, const S2CellId& id,
+ string_view prefix) const {
+ // There are generally more ancestor terms than covering terms, so we add
+ // the extra "marker" character to the covering terms to distinguish them.
+ if (term_type == TermType::ANCESTOR) {
+ return absl::StrCat(prefix, id.ToToken());
+ } else {
+ return absl::StrCat(prefix, options_.marker(), id.ToToken());
+ }
+}
+
+vector<string> S2RegionTermIndexer::GetIndexTerms(const S2Point& point,
+ string_view prefix) {
+ // See the top of this file for an overview of the indexing strategy.
+ //
+ // The last cell generated by this loop is effectively the covering for
+ // the given point. You might expect that this cell would be indexed as a
+ // covering term, but as an optimization we always index these cells as
+ // ancestor terms only. This is possible because query regions will never
+ // contain a descendant of such cells. Note that this is true even when
+ // max_level() != true_max_level() (see S2RegionCoverer::Options).
+
+ const S2CellId id(point);
+ vector<string> terms;
+ for (int level = options_.min_level(); level <= options_.max_level();
+ level += options_.level_mod()) {
+ terms.push_back(GetTerm(TermType::ANCESTOR, id.parent(level), prefix));
+ }
+ return terms;
+}
+
+vector<string> S2RegionTermIndexer::GetIndexTerms(const S2Region& region,
+ string_view prefix) {
+ // Note that options may have changed since the last call.
+ *coverer_.mutable_options() = options_;
+ S2CellUnion covering = coverer_.GetCovering(region);
+ return GetIndexTermsForCanonicalCovering(covering, prefix);
+}
+
+vector<string> S2RegionTermIndexer::GetIndexTermsForCanonicalCovering(
+ const S2CellUnion& covering, string_view prefix) {
+ // See the top of this file for an overview of the indexing strategy.
+ //
+ // Cells in the covering are normally indexed as covering terms. If we are
+ // optimizing for query time rather than index space, they are also indexed
+ // as ancestor terms (since this lets us reduce the number of terms in the
+ // query). Finally, as an optimization we always index true_max_level()
+ // cells as ancestor cells only, since these cells have the special property
+ // that query regions will never contain a descendant of these cells.
+
+ S2_CHECK(!options_.index_contains_points_only());
+ if (google::DEBUG_MODE) {
+ *coverer_.mutable_options() = options_;
+ S2_CHECK(coverer_.IsCanonical(covering));
+ }
+ vector<string> terms;
+ S2CellId prev_id = S2CellId::None();
+ int true_max_level = options_.true_max_level();
+ for (S2CellId id : covering) {
+ // IsCanonical() already checks the following conditions, but we repeat
+ // them here for documentation purposes.
+ int level = id.level();
+ S2_DCHECK_GE(level, options_.min_level());
+ S2_DCHECK_LE(level, options_.max_level());
+ S2_DCHECK_EQ(0, (level - options_.min_level()) % options_.level_mod());
+
+ if (level < true_max_level) {
+ // Add a covering term for this cell.
+ terms.push_back(GetTerm(TermType::COVERING, id, prefix));
+ }
+ if (level == true_max_level || !options_.optimize_for_space()) {
+ // Add an ancestor term for this cell at the constrained level.
+ terms.push_back(GetTerm(TermType::ANCESTOR, id.parent(level), prefix));
+ }
+ // Finally, add ancestor terms for all the ancestors of this cell.
+ while ((level -= options_.level_mod()) >= options_.min_level()) {
+ S2CellId ancestor_id = id.parent(level);
+ if (prev_id != S2CellId::None() && prev_id.level() > level &&
+ prev_id.parent(level) == ancestor_id) {
+ break; // We have already processed this cell and its ancestors.
+ }
+ terms.push_back(GetTerm(TermType::ANCESTOR, ancestor_id, prefix));
+ }
+ prev_id = id;
+ }
+ return terms;
+}
+
+vector<string> S2RegionTermIndexer::GetQueryTerms(const S2Point& point,
+ string_view prefix) {
+ // See the top of this file for an overview of the indexing strategy.
+
+ const S2CellId id(point);
+ vector<string> terms;
+ // Recall that all true_max_level() cells are indexed only as ancestor terms.
+ int level = options_.true_max_level();
+ terms.push_back(GetTerm(TermType::ANCESTOR, id.parent(level), prefix));
+ if (options_.index_contains_points_only()) return terms;
+
+ // Add covering terms for all the ancestor cells.
+ for (; level >= options_.min_level(); level -= options_.level_mod()) {
+ terms.push_back(GetTerm(TermType::COVERING, id.parent(level), prefix));
+ }
+ return terms;
+}
+
+vector<string> S2RegionTermIndexer::GetQueryTerms(const S2Region& region,
+ string_view prefix) {
+ // Note that options may have changed since the last call.
+ *coverer_.mutable_options() = options_;
+ S2CellUnion covering = coverer_.GetCovering(region);
+ return GetQueryTermsForCanonicalCovering(covering, prefix);
+}
+
+vector<string> S2RegionTermIndexer::GetQueryTermsForCanonicalCovering(
+ const S2CellUnion& covering, string_view prefix) {
+ // See the top of this file for an overview of the indexing strategy.
+
+ if (google::DEBUG_MODE) {
+ *coverer_.mutable_options() = options_;
+ S2_CHECK(coverer_.IsCanonical(covering));
+ }
+ vector<string> terms;
+ S2CellId prev_id = S2CellId::None();
+ int true_max_level = options_.true_max_level();
+ for (S2CellId id : covering) {
+ // IsCanonical() already checks the following conditions, but we repeat
+ // them here for documentation purposes.
+ int level = id.level();
+ S2_DCHECK_GE(level, options_.min_level());
+ S2_DCHECK_LE(level, options_.max_level());
+ S2_DCHECK_EQ(0, (level - options_.min_level()) % options_.level_mod());
+
+ // Cells in the covering are always queried as ancestor terms.
+ terms.push_back(GetTerm(TermType::ANCESTOR, id, prefix));
+
+ // If the index only contains points, there are no covering terms.
+ if (options_.index_contains_points_only()) continue;
+
+ // If we are optimizing for index space rather than query time, cells are
+ // also queried as covering terms (except for true_max_level() cells,
+ // which are indexed and queried as ancestor cells only).
+ if (options_.optimize_for_space() && level < true_max_level) {
+ terms.push_back(GetTerm(TermType::COVERING, id, prefix));
+ }
+ // Finally, add covering terms for all the ancestors of this cell.
+ while ((level -= options_.level_mod()) >= options_.min_level()) {
+ S2CellId ancestor_id = id.parent(level);
+ if (prev_id != S2CellId::None() && prev_id.level() > level &&
+ prev_id.parent(level) == ancestor_id) {
+ break; // We have already processed this cell and its ancestors.
+ }
+ terms.push_back(GetTerm(TermType::COVERING, ancestor_id, prefix));
+ }
+ prev_id = id;
+ }
+ return terms;
+}
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2region_union.h"
+
+#include "s2/s2cap.h"
+#include "s2/s2latlng_rect.h"
+
+using std::vector;
+
+S2RegionUnion::S2RegionUnion(vector<std::unique_ptr<S2Region>> regions) {
+ Init(std::move(regions));
+}
+
+void S2RegionUnion::Init(vector<std::unique_ptr<S2Region>> regions) {
+ S2_DCHECK(regions_.empty());
+ regions_ = std::move(regions);
+}
+
+S2RegionUnion::S2RegionUnion(const S2RegionUnion& src)
+ : regions_(src.num_regions()) {
+ for (int i = 0; i < num_regions(); ++i) {
+ regions_[i].reset(src.region(i)->Clone());
+ }
+}
+
+vector<std::unique_ptr<S2Region>> S2RegionUnion::Release() {
+ vector<std::unique_ptr<S2Region>> result;
+ result.swap(regions_);
+ return result;
+}
+
+void S2RegionUnion::Add(std::unique_ptr<S2Region> region) {
+ regions_.push_back(std::move(region));
+}
+
+S2RegionUnion* S2RegionUnion::Clone() const {
+ return new S2RegionUnion(*this);
+}
+
+S2Cap S2RegionUnion::GetCapBound() const {
+ // TODO(ericv): This could be optimized to return a tighter bound,
+ // but doesn't seem worth it unless profiling shows otherwise.
+ return GetRectBound().GetCapBound();
+}
+
+S2LatLngRect S2RegionUnion::GetRectBound() const {
+ S2LatLngRect result = S2LatLngRect::Empty();
+ for (int i = 0; i < num_regions(); ++i) {
+ result = result.Union(region(i)->GetRectBound());
+ }
+ return result;
+}
+
+bool S2RegionUnion::Contains(const S2Cell& cell) const {
+ // Note that this method is allowed to return false even if the cell
+ // is contained by the region.
+ for (int i = 0; i < num_regions(); ++i) {
+ if (region(i)->Contains(cell)) return true;
+ }
+ return false;
+}
+
+bool S2RegionUnion::Contains(const S2Point& p) const {
+ for (int i = 0; i < num_regions(); ++i) {
+ if (region(i)->Contains(p)) return true;
+ }
+ return false;
+}
+
+bool S2RegionUnion::MayIntersect(const S2Cell& cell) const {
+ for (int i = 0; i < num_regions(); ++i) {
+ if (region(i)->MayIntersect(cell)) return true;
+ }
+ return false;
+}
--- /dev/null
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2shape_index.h"
+
+bool S2ClippedShape::ContainsEdge(int id) const {
+ // Linear search is fast because the number of edges per shape is typically
+ // very small (less than 10).
+ for (int e = 0; e < num_edges(); ++e) {
+ if (edge(e) == id) return true;
+ }
+ return false;
+}
+
+S2ShapeIndexCell::~S2ShapeIndexCell() {
+ // Free memory for all shapes owned by this cell.
+ for (S2ClippedShape& s : shapes_)
+ s.Destruct();
+ shapes_.clear();
+}
+
+const S2ClippedShape*
+S2ShapeIndexCell::find_clipped(int shape_id) const {
+ // Linear search is fine because the number of shapes per cell is typically
+ // very small (most often 1), and is large only for pathological inputs
+ // (e.g. very deeply nested loops).
+ for (const auto& s : shapes_) {
+ if (s.shape_id() == shape_id) return &s;
+ }
+ return nullptr;
+}
+
+// Allocate room for "n" additional clipped shapes in the cell, and return a
+// pointer to the first new clipped shape. Expects that all new clipped
+// shapes will have a larger shape id than any current shape, and that shapes
+// will be added in increasing shape id order.
+S2ClippedShape* S2ShapeIndexCell::add_shapes(int n) {
+ int size = shapes_.size();
+ shapes_.resize(size + n);
+ return &shapes_[size];
+}
+
+void S2ShapeIndexCell::Encode(int num_shape_ids, Encoder* encoder) const {
+ // The encoding is designed to be especially compact in certain common
+ // situations:
+ //
+ // 1. The S2ShapeIndex contains exactly one shape.
+ //
+ // 2. The S2ShapeIndex contains more than one shape, but a particular index
+ // cell contains only one shape (num_clipped == 1).
+ //
+ // 3. The edge ids for a given shape in a cell form a contiguous range.
+ //
+ // The details were optimized by constructing an S2ShapeIndex for each
+ // feature in Google's geographic repository and measuring their total
+ // encoded size. The MutableS2ShapeIndex encoding (of which this function
+ // is just one part) uses an average of 1.88 bytes per vertex for features
+ // consisting of polygons or polylines.
+ //
+ // Note that this code does not bother handling num_shapes >= 2**28 or
+ // num_edges >= 2**29. This could be fixed using varint64 in a few more
+ // places, but if a single cell contains this many shapes or edges then we
+ // have bigger problems than just the encoding format :)
+ if (num_shape_ids == 1) {
+ // If the entire S2ShapeIndex contains just one shape, then we don't need
+ // to encode any shape ids. This is a very important and common case.
+ S2_DCHECK_EQ(num_clipped(), 1); // Index invariant: no empty cells.
+ const S2ClippedShape& clipped = this->clipped(0);
+ S2_DCHECK_EQ(clipped.shape_id(), 0);
+ int n = clipped.num_edges();
+ encoder->Ensure(Varint::kMax64 + n * Varint::kMax32);
+ if (n >= 2 && n <= 17 && clipped.edge(n - 1) - clipped.edge(0) == n - 1) {
+ // The cell contains a contiguous range of edges (*most common case*).
+ // If the starting edge id is small then we can encode the cell in one
+ // byte. (The n == 0 and n == 1 cases are encoded compactly below.)
+ // This encoding uses a 1-bit tag because it is by far the most common.
+ //
+ // Encoding: bit 0: 0
+ // bit 1: contains_center
+ // bits 2-5: (num_edges - 2)
+ // bits 6+: edge_id
+ encoder->put_varint64(static_cast<uint64>(clipped.edge(0)) << 6 |
+ (n - 2) << 2 | clipped.contains_center() << 1 | 0);
+ } else if (n == 1) {
+ // The cell contains only one edge. For edge ids up to 15, we can
+ // encode the cell in a single byte.
+ //
+ // Encoding: bits 0-1: 1
+ // bit 2: contains_center
+ // bits 3+: edge_id
+ encoder->put_varint64(static_cast<uint64>(clipped.edge(0)) << 3 |
+ clipped.contains_center() << 2 | 1);
+ } else {
+ // General case (including n == 0, which is encoded compactly here).
+ //
+ // Encoding: bits 0-1: 3
+ // bit 2: contains_center
+ // bits 3+: num_edges
+ encoder->put_varint64(static_cast<uint64>(n) << 3 |
+ clipped.contains_center() << 2 | 3);
+ EncodeEdges(clipped, encoder);
+ }
+ } else {
+ if (num_clipped() > 1) {
+ // The cell contains more than one shape. The tag for this encoding
+ // must be distinguishable from the cases encoded below. We can afford
+ // to use a 3-bit tag because num_clipped is generally small.
+ encoder->Ensure(Varint::kMax32);
+ encoder->put_varint32((num_clipped() << 3) | 3);
+ }
+ // The shape ids are delta-encoded.
+ int shape_id_base = 0;
+ for (int j = 0; j < num_clipped(); ++j) {
+ const S2ClippedShape& clipped = this->clipped(j);
+ S2_DCHECK_GE(clipped.shape_id(), shape_id_base);
+ int shape_delta = clipped.shape_id() - shape_id_base;
+ shape_id_base = clipped.shape_id() + 1;
+
+ // Like the code above except that we also need to encode shape_id(s).
+ // Because of this some choices are slightly different.
+ int n = clipped.num_edges();
+ encoder->Ensure((n + 2) * Varint::kMax32);
+ if (n >= 1 && n <= 16 && clipped.edge(n - 1) - clipped.edge(0) == n - 1) {
+ // The clipped shape has a contiguous range of up to 16 edges. This
+ // encoding uses a 1-bit tag because it is by far the most common.
+ //
+ // Encoding: bit 0: 0
+ // bit 1: contains_center
+ // bits 2+: edge_id
+ // Next value: bits 0-3: (num_edges - 1)
+ // bits 4+: shape_delta
+ encoder->put_varint32(clipped.edge(0) << 2 |
+ clipped.contains_center() << 1 | 0);
+ encoder->put_varint32(shape_delta << 4 | (n - 1));
+ } else if (n == 0) {
+ // Special encoding for clipped shapes with no edges. Such shapes are
+ // common in polygon interiors. This encoding uses a 3-bit tag in
+ // order to leave more bits available for the other encodings.
+ //
+ // NOTE(ericv): When num_clipped > 1, this tag could be 2 bits
+ // (because the tag used to indicate num_clipped > 1 can't appear).
+ // Alternatively, that tag can be considered reserved for future use.
+ //
+ // Encoding: bits 0-2: 7
+ // bit 3: contains_center
+ // bits 4+: shape_delta
+ encoder->put_varint32(shape_delta << 4 |
+ clipped.contains_center() << 3 | 7);
+ } else {
+ // General case. This encoding uses a 2-bit tag, and the first value
+ // typically is encoded into one byte.
+ //
+ // Encoding: bits 0-1: 1
+ // bit 2: contains_center
+ // bits 3+: (num_edges - 1)
+ // Next value: shape_delta
+ encoder->put_varint32((n - 1) << 3 |
+ clipped.contains_center() << 2 | 1);
+ encoder->put_varint32(shape_delta);
+ EncodeEdges(clipped, encoder);
+ }
+ }
+ }
+}
+
+bool S2ShapeIndexCell::Decode(int num_shape_ids, Decoder* decoder) {
+ // This function inverts the encodings documented above.
+ if (num_shape_ids == 1) {
+ // Entire S2ShapeIndex contains only one shape.
+ S2ClippedShape* clipped = add_shapes(1);
+ uint64 header;
+ if (!decoder->get_varint64(&header)) return false;
+ if ((header & 1) == 0) {
+ // The cell contains a contiguous range of edges.
+ int num_edges = ((header >> 2) & 15) + 2;
+ clipped->Init(0 /*shape_id*/, num_edges);
+ clipped->set_contains_center((header & 2) != 0);
+ for (int i = 0, edge_id = header >> 6; i < num_edges; ++i) {
+ clipped->set_edge(i, edge_id + i);
+ }
+ return true;
+ }
+ if ((header & 2) == 0) {
+ // The cell contains a single edge.
+ clipped->Init(0 /*shape_id*/, 1 /*num_edges*/);
+ clipped->set_contains_center((header & 4) != 0);
+ clipped->set_edge(0, header >> 3);
+ return true;
+ }
+ // The cell contains some other combination of edges.
+ int num_edges = header >> 3;
+ clipped->Init(0 /*shape_id*/, num_edges);
+ clipped->set_contains_center((header & 4) != 0);
+ return DecodeEdges(num_edges, clipped, decoder);
+ }
+ // S2ShapeIndex contains more than one shape.
+ uint32 header;
+ if (!decoder->get_varint32(&header)) return false;
+ int num_clipped = 1;
+ if ((header & 7) == 3) {
+ // This cell contains more than one shape.
+ num_clipped = header >> 3;
+ if (!decoder->get_varint32(&header)) return false;
+ }
+ int shape_id = 0;
+ S2ClippedShape* clipped = add_shapes(num_clipped);
+ for (int j = 0; j < num_clipped; ++j, ++clipped, ++shape_id) {
+ if (j > 0 && !decoder->get_varint32(&header)) return false;
+ if ((header & 1) == 0) {
+ // The clipped shape contains a contiguous range of edges.
+ uint32 shape_id_count = 0;
+ if (!decoder->get_varint32(&shape_id_count)) return false;
+ shape_id += shape_id_count >> 4;
+ int num_edges = (shape_id_count & 15) + 1;
+ clipped->Init(shape_id, num_edges);
+ clipped->set_contains_center((header & 2) != 0);
+ for (int i = 0, edge_id = header >> 2; i < num_edges; ++i) {
+ clipped->set_edge(i, edge_id + i);
+ }
+ } else if ((header & 7) == 7) {
+ // The clipped shape has no edges.
+ shape_id += header >> 4;
+ clipped->Init(shape_id, 0);
+ clipped->set_contains_center((header & 8) != 0);
+ } else {
+ // The clipped shape contains some other combination of edges.
+ S2_DCHECK_EQ(header & 3, 1);
+ uint32 shape_delta;
+ if (!decoder->get_varint32(&shape_delta)) return false;
+ shape_id += shape_delta;
+ int num_edges = (header >> 3) + 1;
+ clipped->Init(shape_id, num_edges);
+ clipped->set_contains_center((header & 4) != 0);
+ if (!DecodeEdges(num_edges, clipped, decoder)) return false;
+ }
+ }
+ return true;
+}
+
+inline void S2ShapeIndexCell::EncodeEdges(const S2ClippedShape& clipped,
+ Encoder* encoder) {
+ // Each entry is an (edge_id, count) pair representing a contiguous range of
+ // edges. The edge ids are delta-encoded such that 0 represents the minimum
+ // valid next edge id.
+ //
+ // Encoding: if bits 0-2 < 7: encodes (count - 1)
+ // - bits 3+: edge delta
+ // if bits 0-2 == 7:
+ // - bits 3+ encode (count - 8)
+ // - Next value is edge delta
+ //
+ // No count is encoded for the last edge (saving 3 bits).
+ int edge_id_base = 0;
+ int num_edges = clipped.num_edges();
+ for (int i = 0; i < num_edges; ++i) {
+ int edge_id = clipped.edge(i);
+ S2_DCHECK_GE(edge_id, edge_id_base);
+ int delta = edge_id - edge_id_base;
+ if (i + 1 == num_edges) {
+ // This is the last edge; no need to encode an edge count.
+ encoder->put_varint32(delta);
+ } else {
+ // Count the edges in this contiguous range.
+ int count = 1;
+ for (; i + 1 < num_edges && clipped.edge(i + 1) == edge_id + count; ++i) {
+ ++count;
+ }
+ if (count < 8) {
+ // Count is encoded in low 3 bits of delta.
+ encoder->put_varint32(delta << 3 | (count - 1));
+ } else {
+ // Count and delta are encoded separately.
+ encoder->put_varint32((count - 8) << 3 | 7);
+ encoder->put_varint32(delta);
+ }
+ edge_id_base = edge_id + count;
+ }
+ }
+}
+
+inline bool S2ShapeIndexCell::DecodeEdges(int num_edges,
+ S2ClippedShape* clipped,
+ Decoder* decoder) {
+ // This function inverts the encodings documented above.
+ int32 edge_id = 0;
+ for (int i = 0; i < num_edges; ) {
+ uint32 delta;
+ if (!decoder->get_varint32(&delta)) return false;
+ if (i + 1 == num_edges) {
+ // The last edge is encoded without an edge count.
+ clipped->set_edge(i++, edge_id + delta);
+ } else {
+ // Otherwise decode the count and edge delta.
+ uint32 count = (delta & 7) + 1;
+ delta >>= 3;
+ if (count == 8) {
+ count = delta + 8;
+ if (!decoder->get_varint32(&delta)) return false;
+ }
+ edge_id += delta;
+ for (; count > 0; --count, ++i, ++edge_id) {
+ clipped->set_edge(i, edge_id);
+ }
+ }
+ }
+ return true;
+}
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2shape_index_buffered_region.h"
+
+#include <algorithm>
+#include <vector>
+#include "s2/s2metrics.h"
+#include "s2/s2shape_index_region.h"
+
+using std::min;
+using std::vector;
+
+S2ShapeIndexBufferedRegion::S2ShapeIndexBufferedRegion() {
+}
+
+void S2ShapeIndexBufferedRegion::Init(const S2ShapeIndex* index,
+ S1ChordAngle radius) {
+ radius_ = radius;
+ radius_successor_ = radius.Successor();
+ query_.Init(index);
+ query_.mutable_options()->set_include_interiors(true);
+}
+
+S2ShapeIndexBufferedRegion* S2ShapeIndexBufferedRegion::Clone() const {
+ return new S2ShapeIndexBufferedRegion(&index(), radius_);
+}
+
+S2Cap S2ShapeIndexBufferedRegion::GetCapBound() const {
+ S2Cap orig_cap = MakeS2ShapeIndexRegion(&index()).GetCapBound();
+ return S2Cap(orig_cap.center(), orig_cap.radius() + radius_);
+}
+
+S2LatLngRect S2ShapeIndexBufferedRegion::GetRectBound() const {
+ S2LatLngRect orig_rect = MakeS2ShapeIndexRegion(&index()).GetRectBound();
+ return orig_rect.ExpandedByDistance(radius_.ToAngle());
+}
+
+void S2ShapeIndexBufferedRegion::GetCellUnionBound(vector<S2CellId> *cellids)
+ const {
+ // We start with a covering of the original S2ShapeIndex, and then expand it
+ // by replacing each cell with a block of 4 cells whose union contains the
+ // original cell buffered by the given radius.
+ //
+ // This increases the number of cells in the covering by a factor of 4 and
+ // increases the covered area by a factor of 16, so it is not a very good
+ // covering, but it is much better than always returning the 6 face cells.
+ vector<S2CellId> orig_cellids;
+ MakeS2ShapeIndexRegion(&index()).GetCellUnionBound(&orig_cellids);
+
+ double radians = radius_.ToAngle().radians();
+ int max_level = S2::kMinWidth.GetLevelForMinValue(radians) - 1;
+ if (max_level < 0) {
+ return S2Cap::Full().GetCellUnionBound(cellids);
+ }
+ cellids->clear();
+ for (S2CellId id : orig_cellids) {
+ if (id.is_face()) {
+ return S2Cap::Full().GetCellUnionBound(cellids);
+ }
+ id.AppendVertexNeighbors(min(max_level, id.level() - 1), cellids);
+ }
+}
+
+bool S2ShapeIndexBufferedRegion::Contains(const S2Cell& cell) const {
+ // To implement this method perfectly would require computing the directed
+ // Hausdorff distance, which is expensive (and not currently implemented).
+ // However the following heuristic is almost as good in practice and much
+ // cheaper to compute.
+
+ // Return true if the unbuffered region contains this cell.
+ if (MakeS2ShapeIndexRegion(&index()).Contains(cell)) return true;
+
+ // Otherwise approximate the cell by its bounding cap.
+ //
+ // NOTE(ericv): It would be slightly more accurate to first find the closest
+ // point in the indexed geometry to the cell, and then measure the actual
+ // maximum distance from that point to the cell (a poor man's Hausdorff
+ // distance). But based on actual tests this is not worthwhile.
+ S2Cap cap = cell.GetCapBound();
+ if (radius_ < cap.radius()) return false;
+
+ // Return true if the distance to the cell center plus the radius of the
+ // cell's bounding cap is less than or equal to "radius_".
+ S2ClosestEdgeQuery::PointTarget target(cell.GetCenter());
+ return query_.IsDistanceLess(&target, radius_successor_ - cap.radius());
+}
+
+bool S2ShapeIndexBufferedRegion::MayIntersect(const S2Cell& cell) const {
+ // Return true if the distance is less than or equal to "radius_".
+ S2ClosestEdgeQuery::CellTarget target(cell);
+ return query_.IsDistanceLess(&target, radius_successor_);
+}
+
+bool S2ShapeIndexBufferedRegion::Contains(const S2Point& p) const {
+ S2ClosestEdgeQuery::PointTarget target(p);
+ // Return true if the distance is less than or equal to "radius_".
+ return query_.IsDistanceLess(&target, radius_successor_);
+}
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2shape_index_measures.h"
+
+#include "s2/s2shape_measures.h"
+
+namespace S2 {
+
+int GetDimension(const S2ShapeIndex& index) {
+ int dim = -1;
+ for (int i = 0; i < index.num_shape_ids(); ++i) {
+ S2Shape* shape = index.shape(i);
+ if (shape) dim = std::max(dim, shape->dimension());
+ }
+ return dim;
+}
+
+int GetNumPoints(const S2ShapeIndex& index) {
+ int count = 0;
+ for (int i = 0; i < index.num_shape_ids(); ++i) {
+ S2Shape* shape = index.shape(i);
+ if (shape && shape->dimension() == 0) {
+ count += shape->num_edges();
+ }
+ }
+ return count;
+}
+
+S1Angle GetLength(const S2ShapeIndex& index) {
+ S1Angle length;
+ for (int i = 0; i < index.num_shape_ids(); ++i) {
+ S2Shape* shape = index.shape(i);
+ if (shape) length += S2::GetLength(*shape);
+ }
+ return length;
+}
+
+S1Angle GetPerimeter(const S2ShapeIndex& index) {
+ S1Angle perimeter;
+ for (int i = 0; i < index.num_shape_ids(); ++i) {
+ S2Shape* shape = index.shape(i);
+ if (shape) perimeter += S2::GetPerimeter(*shape);
+ }
+ return perimeter;
+}
+
+double GetArea(const S2ShapeIndex& index) {
+ double area = 0;
+ for (int i = 0; i < index.num_shape_ids(); ++i) {
+ S2Shape* shape = index.shape(i);
+ if (shape) area += S2::GetArea(*shape);
+ }
+ return area;
+}
+
+double GetApproxArea(const S2ShapeIndex& index) {
+ double area = 0;
+ for (int i = 0; i < index.num_shape_ids(); ++i) {
+ S2Shape* shape = index.shape(i);
+ if (shape) area += S2::GetApproxArea(*shape);
+ }
+ return area;
+}
+
+S2Point GetCentroid(const S2ShapeIndex& index) {
+ int dim = GetDimension(index);
+ S2Point centroid;
+ for (int i = 0; i < index.num_shape_ids(); ++i) {
+ S2Shape* shape = index.shape(i);
+ if (shape && shape->dimension() == dim) {
+ centroid += S2::GetCentroid(*shape);
+ }
+ }
+ return centroid;
+}
+
+} // namespace S2
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2shape_measures.h"
+
+#include <cmath>
+#include <vector>
+#include "s2/s2loop_measures.h"
+#include "s2/s2polyline_measures.h"
+
+using std::fabs;
+using std::vector;
+
+namespace S2 {
+
+S1Angle GetLength(const S2Shape& shape) {
+ if (shape.dimension() != 1) return S1Angle::Zero();
+ S1Angle length;
+ vector<S2Point> vertices;
+ int num_chains = shape.num_chains();
+ for (int chain_id = 0; chain_id < num_chains; ++chain_id) {
+ GetChainVertices(shape, chain_id, &vertices);
+ length += S2::GetLength(vertices);
+ }
+ return length;
+}
+
+S1Angle GetPerimeter(const S2Shape& shape) {
+ if (shape.dimension() != 2) return S1Angle::Zero();
+ S1Angle perimeter;
+ vector<S2Point> vertices;
+ int num_chains = shape.num_chains();
+ for (int chain_id = 0; chain_id < num_chains; ++chain_id) {
+ GetChainVertices(shape, chain_id, &vertices);
+ perimeter += S2::GetPerimeter(S2PointLoopSpan(vertices));
+ }
+ return perimeter;
+}
+
+double GetArea(const S2Shape& shape) {
+ if (shape.dimension() != 2) return 0.0;
+
+ // Since S2Shape uses the convention that the interior of the shape is to
+ // the left of all edges, in theory we could compute the area of the polygon
+ // by simply adding up all the loop areas modulo 4*Pi. The problem with
+ // this approach is that polygons holes typically have areas near 4*Pi,
+ // which can create large cancellation errors when computing the area of
+ // small polygons with holes. For example, a shell with an area of 4 square
+ // meters (1e-13 steradians) surrounding a hole with an area of 3 square
+ // meters (7.5e-14 sterians) would lose almost all of its accuracy if the
+ // area of the hole was computed as 12.566370614359098.
+ //
+ // So instead we use S2::GetSignedArea() to ensure that all loops have areas
+ // in the range [-2*Pi, 2*Pi].
+ double area = 0;
+ vector<S2Point> vertices;
+ int num_chains = shape.num_chains();
+ for (int chain_id = 0; chain_id < num_chains; ++chain_id) {
+ GetChainVertices(shape, chain_id, &vertices);
+ area += S2::GetSignedArea(S2PointLoopSpan(vertices));
+ }
+ // Note that S2::GetSignedArea() guarantees that the full loop (containing
+ // all points on the sphere) has a very small negative area.
+ S2_DCHECK_LE(fabs(area), 4 * M_PI);
+ if (area < 0.0) area += 4 * M_PI;
+ return area;
+}
+
+double GetApproxArea(const S2Shape& shape) {
+ if (shape.dimension() != 2) return 0.0;
+
+ double area = 0;
+ vector<S2Point> vertices;
+ int num_chains = shape.num_chains();
+ for (int chain_id = 0; chain_id < num_chains; ++chain_id) {
+ GetChainVertices(shape, chain_id, &vertices);
+ area += S2::GetApproxArea(S2PointLoopSpan(vertices));
+ }
+ // Special case to ensure that full polygons are handled correctly.
+ if (area <= 4 * M_PI) return area;
+ return fmod(area, 4 * M_PI);
+}
+
+S2Point GetCentroid(const S2Shape& shape) {
+ S2Point centroid;
+ vector<S2Point> vertices;
+ int dimension = shape.dimension();
+ int num_chains = shape.num_chains();
+ for (int chain_id = 0; chain_id < num_chains; ++chain_id) {
+ switch (dimension) {
+ case 0:
+ centroid += shape.edge(chain_id).v0;
+ break;
+ case 1:
+ GetChainVertices(shape, chain_id, &vertices);
+ centroid += S2::GetCentroid(S2PointSpan(vertices));
+ break;
+ default:
+ GetChainVertices(shape, chain_id, &vertices);
+ centroid += S2::GetCentroid(S2PointLoopSpan(vertices));
+ break;
+ }
+ }
+ return centroid;
+}
+
+void GetChainVertices(const S2Shape& shape, int chain_id,
+ std::vector<S2Point>* vertices) {
+ S2Shape::Chain chain = shape.chain(chain_id);
+ int num_vertices = chain.length + (shape.dimension() == 1);
+ vertices->clear();
+ vertices->reserve(num_vertices);
+ int e = 0;
+ if (num_vertices & 1) {
+ vertices->push_back(shape.chain_edge(chain_id, e++).v0);
+ }
+ for (; e < num_vertices; e += 2) {
+ auto edge = shape.chain_edge(chain_id, e);
+ vertices->push_back(edge.v0);
+ vertices->push_back(edge.v1);
+ }
+}
+
+} // namespace S2
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2shapeutil_build_polygon_boundaries.h"
+
+#include "s2/util/gtl/btree_map.h"
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/mutable_s2shape_index.h"
+#include "s2/s2contains_point_query.h"
+#include "s2/s2shape_index.h"
+#include "s2/s2shapeutil_contains_brute_force.h"
+
+using absl::WrapUnique;
+using std::vector;
+
+namespace s2shapeutil {
+
+void BuildPolygonBoundaries(const vector<vector<S2Shape*>>& components,
+ vector<vector<S2Shape*>>* polygons) {
+ polygons->clear();
+ if (components.empty()) return;
+
+ // Since the loop boundaries do not cross, a loop nesting hierarchy can be
+ // defined by choosing any point on the sphere as the "point at infinity".
+ // Loop A then contains loop B if (1) A contains the boundary of B and (2)
+ // loop A does not contain the point at infinity.
+ //
+ // We choose S2::Origin() for this purpose. The loop nesting hierarchy then
+ // determines the face structure. Here are the details:
+ //
+ // 1. Build an S2ShapeIndex of all loops that do not contain S2::Origin().
+ // This leaves at most one unindexed loop per connected component
+ // (the "outer loop").
+ //
+ // 2. For each component, choose a representative vertex and determine
+ // which indexed loops contain it. The "depth" of this component is
+ // defined as the number of such loops.
+ //
+ // 3. Assign the outer loop of each component to the containing loop whose
+ // depth is one less. This generates a set of multi-loop polygons.
+ //
+ // 4. The outer loops of all components at depth 0 become a single face.
+
+ MutableS2ShapeIndex index;
+ // A map from shape.id() to the corresponding component number.
+ vector<int> component_ids;
+ vector<S2Shape*> outer_loops;
+ for (int i = 0; i < components.size(); ++i) {
+ const auto& component = components[i];
+ for (S2Shape* loop : component) {
+ if (component.size() > 1 &&
+ !s2shapeutil::ContainsBruteForce(*loop, S2::Origin())) {
+ // Ownership is transferred back at the end of this function.
+ index.Add(WrapUnique(loop));
+ component_ids.push_back(i);
+ } else {
+ outer_loops.push_back(loop);
+ }
+ }
+ // Check that there is exactly one outer loop in each component.
+ S2_DCHECK_EQ(i + 1, outer_loops.size()) << "Component is not a subdivision";
+ }
+ // Find the loops containing each component.
+ vector<vector<S2Shape*>> ancestors(components.size());
+ auto contains_query = MakeS2ContainsPointQuery(&index);
+ for (int i = 0; i < outer_loops.size(); ++i) {
+ auto loop = outer_loops[i];
+ S2_DCHECK_GT(loop->num_edges(), 0);
+ ancestors[i] = contains_query.GetContainingShapes(loop->edge(0).v0);
+ }
+ // Assign each outer loop to the component whose depth is one less.
+ // Components at depth 0 become a single face.
+ gtl::btree_map<S2Shape*, vector<S2Shape*>> children;
+ for (int i = 0; i < outer_loops.size(); ++i) {
+ S2Shape* ancestor = nullptr;
+ int depth = ancestors[i].size();
+ if (depth > 0) {
+ for (auto candidate : ancestors[i]) {
+ if (ancestors[component_ids[candidate->id()]].size() == depth - 1) {
+ S2_DCHECK(ancestor == nullptr);
+ ancestor = candidate;
+ }
+ }
+ S2_DCHECK(ancestor != nullptr);
+ }
+ children[ancestor].push_back(outer_loops[i]);
+ }
+ // There is one face per loop that is not an outer loop, plus one for the
+ // outer loops of components at depth 0.
+ polygons->resize(index.num_shape_ids() + 1);
+ for (int i = 0; i < index.num_shape_ids(); ++i) {
+ auto polygon = &(*polygons)[i];
+ auto loop = index.shape(i);
+ auto itr = children.find(loop);
+ if (itr != children.end()) {
+ *polygon = itr->second;
+ }
+ polygon->push_back(loop);
+ }
+ polygons->back() = children[nullptr];
+
+ // Explicitly release the shapes from the index so they are not deleted.
+ for (auto& ptr : index.ReleaseAll()) ptr.release();
+}
+
+} // namespace s2shapeutil
--- /dev/null
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2shapeutil_coding.h"
+
+#include <memory>
+
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/encoded_s2point_vector.h"
+#include "s2/encoded_string_vector.h"
+#include "s2/s2lax_polygon_shape.h"
+#include "s2/s2lax_polyline_shape.h"
+#include "s2/s2point_vector_shape.h"
+#include "s2/s2polygon.h"
+#include "s2/s2polyline.h"
+
+using absl::make_unique;
+using std::make_shared;
+using std::unique_ptr;
+using std::vector;
+
+using CodingHint = s2coding::CodingHint;
+
+namespace s2shapeutil {
+
+bool FastEncodeShape(const S2Shape& shape, Encoder* encoder) {
+ switch (shape.type_tag()) {
+ case S2Polygon::Shape::kTypeTag: {
+ down_cast<const S2Polygon::Shape*>(&shape)->EncodeUncompressed(encoder);
+ return true;
+ }
+ case S2Polyline::Shape::kTypeTag: {
+ down_cast<const S2Polyline::Shape*>(&shape)->Encode(encoder);
+ return true;
+ }
+ case S2PointVectorShape::kTypeTag: {
+ down_cast<const S2PointVectorShape*>(&shape)->Encode(
+ encoder, CodingHint::FAST);
+ return true;
+ }
+ case S2LaxPolylineShape::kTypeTag: {
+ down_cast<const S2LaxPolylineShape*>(&shape)->Encode(
+ encoder, CodingHint::FAST);
+ return true;
+ }
+ case S2LaxPolygonShape::kTypeTag: {
+ down_cast<const S2LaxPolygonShape*>(&shape)->Encode(
+ encoder, CodingHint::FAST);
+ return true;
+ }
+ default: {
+ S2_LOG(DFATAL) << "Unsupported S2Shape type: " << shape.type_tag();
+ return false;
+ }
+ }
+}
+
+bool CompactEncodeShape(const S2Shape& shape, Encoder* encoder) {
+ switch (shape.type_tag()) {
+ case S2Polygon::Shape::kTypeTag: {
+ down_cast<const S2Polygon::Shape*>(&shape)->Encode(encoder);
+ return true;
+ }
+ case S2PointVectorShape::kTypeTag: {
+ down_cast<const S2PointVectorShape*>(&shape)->Encode(
+ encoder, CodingHint::COMPACT);
+ return true;
+ }
+ case S2LaxPolylineShape::kTypeTag: {
+ down_cast<const S2LaxPolylineShape*>(&shape)->Encode(
+ encoder, CodingHint::COMPACT);
+ return true;
+ }
+ case S2LaxPolygonShape::kTypeTag: {
+ down_cast<const S2LaxPolygonShape*>(&shape)->Encode(
+ encoder, CodingHint::COMPACT);
+ return true;
+ }
+ default: {
+ return FastEncodeShape(shape, encoder);
+ }
+ }
+}
+
+// A ShapeDecoder that fully decodes an S2Shape of the given type. After this
+// function returns, the underlying Decoder data is no longer needed.
+unique_ptr<S2Shape> FullDecodeShape(S2Shape::TypeTag tag, Decoder* decoder) {
+ switch (tag) {
+ case S2Polygon::Shape::kTypeTag: {
+ auto shape = make_unique<S2Polygon::OwningShape>();
+ if (!shape->Init(decoder)) return nullptr;
+ return std::move(shape);
+ }
+ case S2Polyline::Shape::kTypeTag: {
+ auto shape = make_unique<S2Polyline::OwningShape>();
+ if (!shape->Init(decoder)) return nullptr;
+ return std::move(shape);
+ }
+ case S2PointVectorShape::kTypeTag: {
+ auto shape = make_unique<S2PointVectorShape>();
+ if (!shape->Init(decoder)) return nullptr;
+ return std::move(shape);
+ }
+ case S2LaxPolylineShape::kTypeTag: {
+ auto shape = make_unique<S2LaxPolylineShape>();
+ if (!shape->Init(decoder)) return nullptr;
+ return std::move(shape);
+ }
+ case S2LaxPolygonShape::kTypeTag: {
+ auto shape = make_unique<S2LaxPolygonShape>();
+ if (!shape->Init(decoder)) return nullptr;
+ return std::move(shape);
+ }
+ default: {
+ S2_LOG(DFATAL) << "Unsupported S2Shape type: " << tag;
+ return nullptr;
+ }
+ }
+}
+
+unique_ptr<S2Shape> LazyDecodeShape(S2Shape::TypeTag tag, Decoder* decoder) {
+ switch (tag) {
+ case S2PointVectorShape::kTypeTag: {
+ auto shape = make_unique<EncodedS2PointVectorShape>();
+ if (!shape->Init(decoder)) return nullptr;
+ return std::move(shape);
+ }
+ case S2LaxPolylineShape::kTypeTag: {
+ auto shape = make_unique<EncodedS2LaxPolylineShape>();
+ if (!shape->Init(decoder)) return nullptr;
+ return std::move(shape);
+ }
+ case S2LaxPolygonShape::kTypeTag: {
+ auto shape = make_unique<EncodedS2LaxPolygonShape>();
+ if (!shape->Init(decoder)) return nullptr;
+ return std::move(shape);
+ }
+ default: {
+ return FullDecodeShape(tag, decoder);
+ }
+ }
+}
+
+bool EncodeTaggedShapes(const S2ShapeIndex& index,
+ const ShapeEncoder& shape_encoder,
+ Encoder* encoder) {
+ s2coding::StringVectorEncoder shape_vector;
+ for (S2Shape* shape : index) {
+ Encoder* sub_encoder = shape_vector.AddViaEncoder();
+ if (shape == nullptr) continue; // Encode as zero bytes.
+
+ uint32 tag = shape->type_tag();
+ if (tag == S2Shape::kNoTypeTag) {
+ S2_LOG(DFATAL) << "Unsupported S2Shape type: " << tag;
+ return false;
+ }
+ sub_encoder->Ensure(Encoder::kVarintMax32);
+ sub_encoder->put_varint32(tag);
+ shape_encoder(*shape, sub_encoder);
+ }
+ shape_vector.Encode(encoder);
+ return true;
+}
+
+bool FastEncodeTaggedShapes(const S2ShapeIndex& index, Encoder* encoder) {
+ return EncodeTaggedShapes(index, FastEncodeShape, encoder);
+}
+
+bool CompactEncodeTaggedShapes(const S2ShapeIndex& index, Encoder* encoder) {
+ return EncodeTaggedShapes(index, CompactEncodeShape, encoder);
+}
+
+TaggedShapeFactory::TaggedShapeFactory(const ShapeDecoder& shape_decoder,
+ Decoder* decoder)
+ : shape_decoder_(shape_decoder) {
+ if (!encoded_shapes_.Init(decoder)) encoded_shapes_.Clear();
+}
+
+unique_ptr<S2Shape> TaggedShapeFactory::operator[](int shape_id) const {
+ Decoder decoder = encoded_shapes_.GetDecoder(shape_id);
+ S2Shape::TypeTag tag;
+ if (!decoder.get_varint32(&tag)) return nullptr;
+ return shape_decoder_(tag, &decoder);
+}
+
+TaggedShapeFactory FullDecodeShapeFactory(Decoder* decoder) {
+ return TaggedShapeFactory(FullDecodeShape, decoder);
+}
+
+TaggedShapeFactory LazyDecodeShapeFactory(Decoder* decoder) {
+ return TaggedShapeFactory(LazyDecodeShape, decoder);
+}
+
+VectorShapeFactory::VectorShapeFactory(vector<unique_ptr<S2Shape>> shapes)
+ : shared_shapes_(
+ make_shared<vector<unique_ptr<S2Shape>>>(std::move(shapes))) {
+}
+
+unique_ptr<S2Shape> VectorShapeFactory::operator[](int shape_id) const {
+ return std::move((*shared_shapes_)[shape_id]);
+}
+
+VectorShapeFactory SingletonShapeFactory(std::unique_ptr<S2Shape> shape) {
+ vector<unique_ptr<S2Shape>> shapes;
+ shapes.push_back(std::move(shape));
+ return VectorShapeFactory(std::move(shapes));
+}
+
+// An S2Shape that simply wraps some other shape.
+class WrappedShape : public S2Shape {
+ public:
+ explicit WrappedShape(S2Shape* shape) : shape_(*shape) {}
+ // S2Shape interface:
+ int num_edges() const final { return shape_.num_edges(); }
+ Edge edge(int e) const final { return shape_.edge(e); }
+ int dimension() const final { return shape_.dimension(); }
+ ReferencePoint GetReferencePoint() const final {
+ return shape_.GetReferencePoint();
+ }
+ int num_chains() const final { return shape_.num_chains(); }
+ Chain chain(int i) const final { return shape_.chain(i); }
+ Edge chain_edge(int i, int j) const final {
+ return shape_.chain_edge(i, j);
+ }
+ ChainPosition chain_position(int e) const final {
+ return shape_.chain_position(e);
+ }
+
+ private:
+ const S2Shape& shape_;
+};
+
+unique_ptr<S2Shape> WrappedShapeFactory::operator[](int shape_id) const {
+ S2Shape* shape = index_.shape(shape_id);
+ if (shape == nullptr) return nullptr;
+ return make_unique<WrappedShape>(shape);
+}
+
+} // namespace s2shapeutil
--- /dev/null
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2shapeutil_contains_brute_force.h"
+
+#include <utility>
+#include "s2/s2edge_crosser.h"
+
+namespace s2shapeutil {
+
+bool ContainsBruteForce(const S2Shape& shape, const S2Point& point) {
+ if (shape.dimension() < 2) return false;
+
+ S2Shape::ReferencePoint ref_point = shape.GetReferencePoint();
+ if (ref_point.point == point) return ref_point.contained;
+
+ S2CopyingEdgeCrosser crosser(ref_point.point, point);
+ bool inside = ref_point.contained;
+ for (int e = 0; e < shape.num_edges(); ++e) {
+ auto edge = shape.edge(e);
+ inside ^= crosser.EdgeOrVertexCrossing(edge.v0, edge.v1);
+ }
+ return inside;
+}
+
+} // namespace s2shapeutil
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "s2/s2shapeutil_edge_iterator.h"
+
+#include "s2/third_party/absl/strings/str_cat.h"
+
+namespace s2shapeutil {
+
+EdgeIterator::EdgeIterator(const S2ShapeIndex* index)
+ : index_(index), shape_id_(-1), num_edges_(0), edge_id_(-1) {
+ Next();
+}
+
+S2Shape::Edge EdgeIterator::edge() const {
+ S2_DCHECK(!Done());
+ return index_->shape(shape_id_)->edge(edge_id_);
+}
+
+void EdgeIterator::Next() {
+ while (++edge_id_ >= num_edges_) {
+ if (++shape_id_ >= index_->num_shape_ids()) break;
+ S2Shape* shape = index_->shape(shape_id_);
+ num_edges_ = (shape == nullptr) ? 0 : shape->num_edges();
+ edge_id_ = -1;
+ }
+}
+
+string EdgeIterator::DebugString() const {
+ return absl::StrCat("(shape=", shape_id_, ", edge=", edge_id_, ")");
+}
+
+} // namespace s2shapeutil
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2shapeutil_get_reference_point.h"
+
+#include <algorithm>
+
+#include "s2/base/logging.h"
+#include "s2/s2contains_vertex_query.h"
+
+using std::vector;
+using ReferencePoint = S2Shape::ReferencePoint;
+
+namespace s2shapeutil {
+
+// This is a helper function for GetReferencePoint() below.
+//
+// If the given vertex "vtest" is unbalanced (see definition below), sets
+// "result" to a ReferencePoint indicating whther "vtest" is contained and
+// returns true. Otherwise returns false.
+static bool GetReferencePointAtVertex(
+ const S2Shape& shape, const S2Point& vtest, ReferencePoint* result) {
+ // Let P be an unbalanced vertex. Vertex P is defined to be inside the
+ // region if the region contains a particular direction vector starting from
+ // P, namely the direction S2::Ortho(P). This can be calculated using
+ // S2ContainsVertexQuery.
+ S2ContainsVertexQuery contains_query(vtest);
+ int n = shape.num_edges();
+ for (int e = 0; e < n; ++e) {
+ auto edge = shape.edge(e);
+ if (edge.v0 == vtest) contains_query.AddEdge(edge.v1, 1);
+ if (edge.v1 == vtest) contains_query.AddEdge(edge.v0, -1);
+ }
+ int contains_sign = contains_query.ContainsSign();
+ if (contains_sign == 0) {
+ return false; // There are no unmatched edges incident to this vertex.
+ }
+ result->point = vtest;
+ result->contained = contains_sign > 0;
+ return true;
+}
+
+// See documentation in header file.
+S2Shape::ReferencePoint GetReferencePoint(const S2Shape& shape) {
+ S2_DCHECK_EQ(shape.dimension(), 2);
+ if (shape.num_edges() == 0) {
+ // A shape with no edges is defined to be full if and only if it
+ // contains at least one chain.
+ return ReferencePoint::Contained(shape.num_chains() > 0);
+ }
+ // Define a "matched" edge as one that can be paired with a corresponding
+ // reversed edge. Define a vertex as "balanced" if all of its edges are
+ // matched. In order to determine containment, we must find an unbalanced
+ // vertex. Often every vertex is unbalanced, so we start by trying an
+ // arbitrary vertex.
+ auto edge = shape.edge(0);
+ ReferencePoint result;
+ if (GetReferencePointAtVertex(shape, edge.v0, &result)) {
+ return result;
+ }
+ // That didn't work, so now we do some extra work to find an unbalanced
+ // vertex (if any). Essentially we gather a list of edges and a list of
+ // reversed edges, and then sort them. The first edge that appears in one
+ // list but not the other is guaranteed to be unmatched.
+ int n = shape.num_edges();
+ vector<S2Shape::Edge> edges(n), rev_edges(n);
+ for (int i = 0; i < n; ++i) {
+ auto edge = shape.edge(i);
+ edges[i] = edge;
+ rev_edges[i] = S2Shape::Edge(edge.v1, edge.v0);
+ }
+ std::sort(edges.begin(), edges.end());
+ std::sort(rev_edges.begin(), rev_edges.end());
+ for (int i = 0; i < n; ++i) {
+ if (edges[i] < rev_edges[i]) { // edges[i] is unmatched
+ S2_CHECK(GetReferencePointAtVertex(shape, edges[i].v0, &result));
+ return result;
+ }
+ if (rev_edges[i] < edges[i]) { // rev_edges[i] is unmatched
+ S2_CHECK(GetReferencePointAtVertex(shape, rev_edges[i].v0, &result));
+ return result;
+ }
+ }
+ // All vertices are balanced, so this polygon is either empty or full except
+ // for degeneracies. By convention it is defined to be full if it contains
+ // any chain with no edges.
+ for (int i = 0; i < shape.num_chains(); ++i) {
+ if (shape.chain(i).length == 0) return ReferencePoint::Contained(true);
+ }
+ return ReferencePoint::Contained(false);
+}
+
+} // namespace s2shapeutil
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2shapeutil_range_iterator.h"
+
+namespace s2shapeutil {
+
+RangeIterator::RangeIterator(const S2ShapeIndex& index)
+ : it_(&index, S2ShapeIndex::BEGIN) {
+ Refresh();
+}
+
+void RangeIterator::Next() {
+ it_.Next();
+ Refresh();
+}
+
+void RangeIterator::SeekTo(const RangeIterator& target) {
+ it_.Seek(target.range_min());
+ // If the current cell does not overlap "target", it is possible that the
+ // previous cell is the one we are looking for. This can only happen when
+ // the previous cell contains "target" but has a smaller S2CellId.
+ if (it_.done() || it_.id().range_min() > target.range_max()) {
+ if (it_.Prev() && it_.id().range_max() < target.id()) it_.Next();
+ }
+ Refresh();
+}
+
+void RangeIterator::SeekBeyond(const RangeIterator& target) {
+ it_.Seek(target.range_max().next());
+ if (!it_.done() && it_.id().range_min() <= target.range_max()) {
+ it_.Next();
+ }
+ Refresh();
+}
+
+// This method is inline, but is only called by non-inline methods defined in
+// this file. Putting the definition here enforces this requirement.
+inline void RangeIterator::Refresh() {
+ range_min_ = id().range_min();
+ range_max_ = id().range_max();
+}
+
+} // namespace s2shapeutil
--- /dev/null
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2shapeutil_visit_crossing_edge_pairs.h"
+
+#include "s2/s2crossing_edge_query.h"
+#include "s2/s2edge_crosser.h"
+#include "s2/s2error.h"
+#include "s2/s2shapeutil_range_iterator.h"
+#include "s2/s2wedge_relations.h"
+
+using std::vector;
+using ChainPosition = S2Shape::ChainPosition;
+
+namespace s2shapeutil {
+
+// Ensure that we don't usually need to allocate memory when collecting the
+// edges in an S2ShapeIndex cell (which by default have about 10 edges).
+using ShapeEdgeVector = absl::InlinedVector<ShapeEdge, 16>;
+
+// Appends all edges in the given S2ShapeIndexCell to the given vector.
+static void AppendShapeEdges(const S2ShapeIndex& index,
+ const S2ShapeIndexCell& cell,
+ ShapeEdgeVector* shape_edges) {
+ for (int s = 0; s < cell.num_clipped(); ++s) {
+ const S2ClippedShape& clipped = cell.clipped(s);
+ const S2Shape& shape = *index.shape(clipped.shape_id());
+ int num_edges = clipped.num_edges();
+ for (int i = 0; i < num_edges; ++i) {
+ shape_edges->push_back(ShapeEdge(shape, clipped.edge(i)));
+ }
+ }
+}
+
+// Returns a vector containing all edges in the given S2ShapeIndexCell.
+// (The result is returned as an output parameter so that the same storage can
+// be reused, rather than allocating a new temporary vector each time.)
+inline static void GetShapeEdges(const S2ShapeIndex& index,
+ const S2ShapeIndexCell& cell,
+ ShapeEdgeVector* shape_edges) {
+ shape_edges->clear();
+ AppendShapeEdges(index, cell, shape_edges);
+}
+
+// Returns a vector containing all edges in the given S2ShapeIndexCell vector.
+// (The result is returned as an output parameter so that the same storage can
+// be reused, rather than allocating a new temporary vector each time.)
+inline static void GetShapeEdges(const S2ShapeIndex& index,
+ const vector<const S2ShapeIndexCell*>& cells,
+ ShapeEdgeVector* shape_edges) {
+ shape_edges->clear();
+ for (auto cell : cells) {
+ AppendShapeEdges(index, *cell, shape_edges);
+ }
+}
+
+// Given a vector of edges within an S2ShapeIndexCell, visit all pairs of
+// crossing edges (of the given CrossingType).
+static bool VisitCrossings(const ShapeEdgeVector& shape_edges,
+ CrossingType type, bool need_adjacent,
+ const EdgePairVisitor& visitor) {
+ const int min_crossing_sign = (type == CrossingType::INTERIOR) ? 1 : 0;
+ int num_edges = shape_edges.size();
+ for (int i = 0; i + 1 < num_edges; ++i) {
+ const ShapeEdge& a = shape_edges[i];
+ int j = i + 1;
+ // A common situation is that an edge AB is followed by an edge BC. We
+ // only need to visit such crossings if "need_adjacent" is true (even if
+ // AB and BC belong to different edge chains).
+ if (!need_adjacent && a.v1() == shape_edges[j].v0()) {
+ if (++j >= num_edges) break;
+ }
+ S2EdgeCrosser crosser(&a.v0(), &a.v1());
+ for (; j < num_edges; ++j) {
+ const ShapeEdge& b = shape_edges[j];
+ if (crosser.c() == nullptr || *crosser.c() != b.v0()) {
+ crosser.RestartAt(&b.v0());
+ }
+ int sign = crosser.CrossingSign(&b.v1());
+ if (sign >= min_crossing_sign) {
+ if (!visitor(a, b, sign == 1)) return false;
+ }
+ }
+ }
+ return true;
+}
+
+// Visits all pairs of crossing edges in the given S2ShapeIndex, terminating
+// early if the given EdgePairVisitor function returns false (in which case
+// VisitCrossings returns false as well). "type" indicates whether all
+// crossings should be visited, or only interior crossings.
+//
+// If "need_adjacent" is false, then edge pairs of the form (AB, BC) may
+// optionally be ignored (even if the two edges belong to different edge
+// chains). This option exists for the benefit of FindSelfIntersection(),
+// which does not need such edge pairs (see below).
+static bool VisitCrossings(
+ const S2ShapeIndex& index, CrossingType type, bool need_adjacent,
+ const EdgePairVisitor& visitor) {
+ // TODO(ericv): Use brute force if the total number of edges is small enough
+ // (using a larger threshold if the S2ShapeIndex is not constructed yet).
+ ShapeEdgeVector shape_edges;
+ for (S2ShapeIndex::Iterator it(&index, S2ShapeIndex::BEGIN);
+ !it.done(); it.Next()) {
+ GetShapeEdges(index, it.cell(), &shape_edges);
+ if (!VisitCrossings(shape_edges, type, need_adjacent, visitor)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool VisitCrossingEdgePairs(const S2ShapeIndex& index, CrossingType type,
+ const EdgePairVisitor& visitor) {
+ const bool need_adjacent = (type == CrossingType::ALL);
+ return VisitCrossings(index, type, need_adjacent, visitor);
+}
+
+//////////////////////////////////////////////////////////////////////
+
+// IndexCrosser is a helper class for finding the edge crossings between a
+// pair of S2ShapeIndexes. It is instantiated twice, once for the index pair
+// (A,B) and once for the index pair (B,A), in order to be able to test edge
+// crossings in the most efficient order.
+namespace {
+class IndexCrosser {
+ public:
+ // If "swapped" is true, the loops A and B have been swapped. This affects
+ // how arguments are passed to the given loop relation, since for example
+ // A.Contains(B) is not the same as B.Contains(A).
+ IndexCrosser(const S2ShapeIndex& a_index, const S2ShapeIndex& b_index,
+ CrossingType type, const EdgePairVisitor& visitor, bool swapped)
+ : a_index_(a_index), b_index_(b_index), visitor_(visitor),
+ min_crossing_sign_(type == CrossingType::INTERIOR ? 1 : 0),
+ swapped_(swapped), b_query_(&b_index_) {
+ }
+
+ // Given two iterators positioned such that ai->id().Contains(bi->id()),
+ // visits all crossings between edges of A and B that intersect a->id().
+ // Terminates early and returns false if visitor_ returns false.
+ // Advances both iterators past ai->id().
+ bool VisitCrossings(RangeIterator* ai, RangeIterator* bi);
+
+ // Given two index cells, visits all crossings between edges of those cells.
+ // Terminates early and returns false if visitor_ returns false.
+ bool VisitCellCellCrossings(const S2ShapeIndexCell& a_cell,
+ const S2ShapeIndexCell& b_cell);
+
+ private:
+ bool VisitEdgePair(const ShapeEdge& a, const ShapeEdge& b, bool is_interior);
+
+ // Visits all crossings of the current edge with all edges of the given index
+ // cell of B. Terminates early and returns false if visitor_ returns false.
+ bool VisitEdgeCellCrossings(const ShapeEdge& a,
+ const S2ShapeIndexCell& b_cell);
+
+ // Visits all crossings of any edge in "a_cell" with any index cell of B that
+ // is a descendant of "b_id". Terminates early and returns false if
+ // visitor_ returns false.
+ bool VisitSubcellCrossings(const S2ShapeIndexCell& a_cell, S2CellId b_id);
+
+ // Visits all crossings of any edge in "a_edges" with any edge in "b_edges".
+ bool VisitEdgesEdgesCrossings(const ShapeEdgeVector& a_edges,
+ const ShapeEdgeVector& b_edges);
+
+ const S2ShapeIndex& a_index_;
+ const S2ShapeIndex& b_index_;
+ const EdgePairVisitor& visitor_;
+ const int min_crossing_sign_;
+ const bool swapped_;
+
+ // Temporary data declared here to avoid repeated memory allocations.
+ S2CrossingEdgeQuery b_query_;
+ vector<const S2ShapeIndexCell*> b_cells_;
+ ShapeEdgeVector a_shape_edges_;
+ ShapeEdgeVector b_shape_edges_;
+};
+} // namespace
+
+inline bool IndexCrosser::VisitEdgePair(const ShapeEdge& a, const ShapeEdge& b,
+ bool is_interior) {
+ if (swapped_) {
+ return visitor_(b, a, is_interior);
+ } else {
+ return visitor_(a, b, is_interior);
+ }
+}
+
+bool IndexCrosser::VisitEdgeCellCrossings(const ShapeEdge& a,
+ const S2ShapeIndexCell& b_cell) {
+ // Test the current edge of A against all edges of "b_cell".
+
+ // Note that we need to use a new S2EdgeCrosser (or call Init) whenever we
+ // replace the contents of b_shape_edges_, since S2EdgeCrosser requires that
+ // its S2Point arguments point to values that persist between Init() calls.
+ GetShapeEdges(b_index_, b_cell, &b_shape_edges_);
+ S2EdgeCrosser crosser(&a.v0(), &a.v1());
+ for (const ShapeEdge& b : b_shape_edges_) {
+ if (crosser.c() == nullptr || *crosser.c() != b.v0()) {
+ crosser.RestartAt(&b.v0());
+ }
+ int sign = crosser.CrossingSign(&b.v1());
+ if (sign >= min_crossing_sign_) {
+ if (!VisitEdgePair(a, b, sign == 1)) return false;
+ }
+ }
+ return true;
+}
+
+bool IndexCrosser::VisitSubcellCrossings(const S2ShapeIndexCell& a_cell,
+ S2CellId b_id) {
+ // Test all edges of "a_cell" against the edges contained in B index cells
+ // that are descendants of "b_id".
+ GetShapeEdges(a_index_, a_cell, &a_shape_edges_);
+ S2PaddedCell b_root(b_id, 0);
+ for (const ShapeEdge& a : a_shape_edges_) {
+ // Use an S2CrossingEdgeQuery starting at "b_root" to find the index cells
+ // of B that might contain crossing edges.
+ if (!b_query_.VisitCells(
+ a.v0(), a.v1(), b_root, [&a, this](const S2ShapeIndexCell& cell) {
+ return VisitEdgeCellCrossings(a, cell);
+ })) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool IndexCrosser::VisitEdgesEdgesCrossings(const ShapeEdgeVector& a_edges,
+ const ShapeEdgeVector& b_edges) {
+ // Test all edges of "a_edges" against all edges of "b_edges".
+ for (const ShapeEdge& a : a_edges) {
+ S2EdgeCrosser crosser(&a.v0(), &a.v1());
+ for (const ShapeEdge& b : b_edges) {
+ if (crosser.c() == nullptr || *crosser.c() != b.v0()) {
+ crosser.RestartAt(&b.v0());
+ }
+ int sign = crosser.CrossingSign(&b.v1());
+ if (sign >= min_crossing_sign_) {
+ if (!VisitEdgePair(a, b, sign == 1)) return false;
+ }
+ }
+ }
+ return true;
+}
+
+inline bool IndexCrosser::VisitCellCellCrossings(
+ const S2ShapeIndexCell& a_cell, const S2ShapeIndexCell& b_cell) {
+ // Test all edges of "a_cell" against all edges of "b_cell".
+ GetShapeEdges(a_index_, a_cell, &a_shape_edges_);
+ GetShapeEdges(b_index_, b_cell, &b_shape_edges_);
+ return VisitEdgesEdgesCrossings(a_shape_edges_, b_shape_edges_);
+}
+
+bool IndexCrosser::VisitCrossings(RangeIterator* ai, RangeIterator* bi) {
+ S2_DCHECK(ai->id().contains(bi->id()));
+ if (ai->cell().num_edges() == 0) {
+ // Skip over the cells of B using binary search.
+ bi->SeekBeyond(*ai);
+ } else {
+ // If ai->id() intersects many edges of B, then it is faster to use
+ // S2CrossingEdgeQuery to narrow down the candidates. But if it
+ // intersects only a few edges, it is faster to check all the crossings
+ // directly. We handle this by advancing "bi" and keeping track of how
+ // many edges we would need to test.
+ static const int kEdgeQueryMinEdges = 23;
+ int b_edges = 0;
+ b_cells_.clear();
+ do {
+ int cell_edges = bi->cell().num_edges();
+ if (cell_edges > 0) {
+ b_edges += cell_edges;
+ if (b_edges >= kEdgeQueryMinEdges) {
+ // There are too many edges, so use an S2CrossingEdgeQuery.
+ if (!VisitSubcellCrossings(ai->cell(), ai->id())) return false;
+ bi->SeekBeyond(*ai);
+ return true;
+ }
+ b_cells_.push_back(&bi->cell());
+ }
+ bi->Next();
+ } while (bi->id() <= ai->range_max());
+ if (!b_cells_.empty()) {
+ // Test all the edge crossings directly.
+ GetShapeEdges(a_index_, ai->cell(), &a_shape_edges_);
+ GetShapeEdges(b_index_, b_cells_, &b_shape_edges_);
+ if (!VisitEdgesEdgesCrossings(a_shape_edges_, b_shape_edges_)) {
+ return false;
+ }
+ }
+ }
+ ai->Next();
+ return true;
+}
+
+bool VisitCrossingEdgePairs(const S2ShapeIndex& a_index,
+ const S2ShapeIndex& b_index,
+ CrossingType type, const EdgePairVisitor& visitor) {
+ // We look for S2CellId ranges where the indexes of A and B overlap, and
+ // then test those edges for crossings.
+
+ // TODO(ericv): Use brute force if the total number of edges is small enough
+ // (using a larger threshold if the S2ShapeIndex is not constructed yet).
+ RangeIterator ai(a_index), bi(b_index);
+ IndexCrosser ab(a_index, b_index, type, visitor, false); // Tests A against B
+ IndexCrosser ba(b_index, a_index, type, visitor, true); // Tests B against A
+ while (!ai.done() || !bi.done()) {
+ if (ai.range_max() < bi.range_min()) {
+ // The A and B cells don't overlap, and A precedes B.
+ ai.SeekTo(bi);
+ } else if (bi.range_max() < ai.range_min()) {
+ // The A and B cells don't overlap, and B precedes A.
+ bi.SeekTo(ai);
+ } else {
+ // One cell contains the other. Determine which cell is larger.
+ int64 ab_relation = ai.id().lsb() - bi.id().lsb();
+ if (ab_relation > 0) {
+ // A's index cell is larger.
+ if (!ab.VisitCrossings(&ai, &bi)) return false;
+ } else if (ab_relation < 0) {
+ // B's index cell is larger.
+ if (!ba.VisitCrossings(&bi, &ai)) return false;
+ } else {
+ // The A and B cells are the same.
+ if (ai.cell().num_edges() > 0 && bi.cell().num_edges() > 0) {
+ if (!ab.VisitCellCellCrossings(ai.cell(), bi.cell())) return false;
+ }
+ ai.Next();
+ bi.Next();
+ }
+ }
+ }
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////
+
+// Helper function that formats a loop error message. If the loop belongs to
+// a multi-loop polygon, adds a prefix indicating which loop is affected.
+static void InitLoopError(S2Error::Code code, const char* format,
+ ChainPosition ap, ChainPosition bp,
+ bool is_polygon, S2Error* error) {
+ error->Init(code, format, ap.offset, bp.offset);
+ if (is_polygon) {
+ error->Init(code, "Loop %d: %s", ap.chain_id, error->text().c_str());
+ }
+}
+
+// Given two loop edges that cross (including at a shared vertex), return true
+// if there is a crossing error and set "error" to a human-readable message.
+static bool FindCrossingError(const S2Shape& shape,
+ const ShapeEdge& a, const ShapeEdge& b,
+ bool is_interior, S2Error* error) {
+ bool is_polygon = shape.num_chains() > 1;
+ S2Shape::ChainPosition ap = shape.chain_position(a.id().edge_id);
+ S2Shape::ChainPosition bp = shape.chain_position(b.id().edge_id);
+ if (is_interior) {
+ if (ap.chain_id != bp.chain_id) {
+ error->Init(S2Error::POLYGON_LOOPS_CROSS,
+ "Loop %d edge %d crosses loop %d edge %d",
+ ap.chain_id, ap.offset, bp.chain_id, bp.offset);
+ } else {
+ InitLoopError(S2Error::LOOP_SELF_INTERSECTION,
+ "Edge %d crosses edge %d", ap, bp, is_polygon, error);
+ }
+ return true;
+ }
+ // Loops are not allowed to have duplicate vertices, and separate loops
+ // are not allowed to share edges or cross at vertices. We only need to
+ // check a given vertex once, so we also require that the two edges have
+ // the same end vertex.
+ if (a.v1() != b.v1()) return false;
+ if (ap.chain_id == bp.chain_id) {
+ InitLoopError(S2Error::DUPLICATE_VERTICES,
+ "Edge %d has duplicate vertex with edge %d",
+ ap, bp, is_polygon, error);
+ return true;
+ }
+ int a_len = shape.chain(ap.chain_id).length;
+ int b_len = shape.chain(bp.chain_id).length;
+ int a_next = (ap.offset + 1 == a_len) ? 0 : ap.offset + 1;
+ int b_next = (bp.offset + 1 == b_len) ? 0 : bp.offset + 1;
+ S2Point a2 = shape.chain_edge(ap.chain_id, a_next).v1;
+ S2Point b2 = shape.chain_edge(bp.chain_id, b_next).v1;
+ if (a.v0() == b.v0() || a.v0() == b2) {
+ // The second edge index is sometimes off by one, hence "near".
+ error->Init(S2Error::POLYGON_LOOPS_SHARE_EDGE,
+ "Loop %d edge %d has duplicate near loop %d edge %d",
+ ap.chain_id, ap.offset, bp.chain_id, bp.offset);
+ return true;
+ }
+ // Since S2ShapeIndex loops are oriented such that the polygon interior is
+ // always on the left, we need to handle the case where one wedge contains
+ // the complement of the other wedge. This is not specifically detected by
+ // GetWedgeRelation, so there are two cases to check for.
+ //
+ // Note that we don't need to maintain any state regarding loop crossings
+ // because duplicate edges are detected and rejected above.
+ if (S2::GetWedgeRelation(a.v0(), a.v1(), a2, b.v0(), b2) ==
+ S2::WEDGE_PROPERLY_OVERLAPS &&
+ S2::GetWedgeRelation(a.v0(), a.v1(), a2, b2, b.v0()) ==
+ S2::WEDGE_PROPERLY_OVERLAPS) {
+ error->Init(S2Error::POLYGON_LOOPS_CROSS,
+ "Loop %d edge %d crosses loop %d edge %d",
+ ap.chain_id, ap.offset, bp.chain_id, bp.offset);
+ return true;
+ }
+ return false;
+}
+
+bool FindSelfIntersection(const S2ShapeIndex& index, S2Error* error) {
+ if (index.num_shape_ids() == 0) return false;
+ S2_DCHECK_EQ(1, index.num_shape_ids());
+ const S2Shape& shape = *index.shape(0);
+
+ // Visit all crossing pairs except possibly for ones of the form (AB, BC),
+ // since such pairs are very common and FindCrossingError() only needs pairs
+ // of the form (AB, AC).
+ return !VisitCrossings(
+ index, CrossingType::ALL, false /*need_adjacent*/,
+ [&](const ShapeEdge& a, const ShapeEdge& b, bool is_interior) {
+ return !FindCrossingError(shape, a, b, is_interior, error);
+ });
+}
+
+} // namespace s2shapeutil
--- /dev/null
+#include "cpp-compat.h"
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2testing.h"
+
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+#include <cstdlib>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "s2/base/commandlineflags.h"
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+#include "s2/r1interval.h"
+#include "s2/s1angle.h"
+#include "s2/s1interval.h"
+#include "s2/s2cap.h"
+#include "s2/s2cell.h"
+#include "s2/s2cell_union.h"
+#include "s2/s2latlng.h"
+#include "s2/s2latlng_rect.h"
+#include "s2/s2loop.h"
+#include "s2/s2pointutil.h"
+#include "s2/s2polygon.h"
+#include "s2/s2polyline.h"
+#include "s2/s2region.h"
+#include "s2/s2text_format.h"
+#include "s2/strings/serialize.h"
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/third_party/absl/strings/str_split.h"
+#include "s2/util/math/matrix3x3.h"
+
+using absl::make_unique;
+using std::max;
+using std::unique_ptr;
+using std::vector;
+
+DEFINE_int32(s2_random_seed, 1,
+ "Seed value that can be passed to S2Testing::rnd.Reset()");
+
+const double S2Testing::kEarthRadiusKm = 6371.01;
+
+S2Testing::Random::Random() {
+ // Unfortunately we can't use FLAGS_s2_random_seed here, because the default
+ // S2Testing::Random instance is initialized before command-line flags have
+ // been parsed.
+ cpp_compat_srandom(1);
+}
+
+void S2Testing::Random::Reset(int seed) {
+ cpp_compat_srandom(seed);
+}
+
+// Return a 64-bit unsigned integer whose lowest "num_bits" are random, and
+// whose other bits are zero.
+inline uint64 GetBits(int num_bits) {
+ S2_DCHECK_GE(num_bits, 0);
+ S2_DCHECK_LE(num_bits, 64);
+
+ // This code uses random(), which returns an integer in the range
+ // from 0 to (2^31)-1 inclusive (i.e. all of the lower 31 bits are
+ // in play, and the 32nd bit and higher are 0) regardless of whether
+ // its return type (long) is larger than 32 bits. See
+ //
+ // www.gnu.org/software/libc/manual/html_node/BSD-Random.html#BSD-Random
+ //
+ // Note that at some point the manual page in linux claimed that the range
+ // is 0 to RAND_MAX as defined in stdlib.h. RAND_MAX however is part only
+ // of the ISO rand() interface. At least as of glibc-2.21, rand() is
+ // simply an alias for random(). On other systems, rand() may differ,
+ // but random() should always adhere to the behavior specified in BSD.
+ static const int RAND_BITS = 31;
+
+ uint64 result = 0;
+ for (int bits = 0; bits < num_bits; bits += RAND_BITS) {
+ result = (result << RAND_BITS) + cpp_compat_random();
+ }
+ if (num_bits < 64) { // Not legal to shift by full bitwidth of type
+ result &= ((1ULL << num_bits) - 1);
+ }
+ return result;
+}
+
+uint64 S2Testing::Random::Rand64() {
+ return GetBits(64);
+}
+
+uint32 S2Testing::Random::Rand32() {
+ return GetBits(32);
+}
+
+double S2Testing::Random::RandDouble() {
+ const int NUM_BITS = 53;
+ return ldexp((double) GetBits(NUM_BITS), -NUM_BITS);
+}
+
+int32 S2Testing::Random::Uniform(int32 n) {
+ S2_DCHECK_GT(n, 0);
+ return static_cast<uint32>(RandDouble() * n);
+}
+
+double S2Testing::Random::UniformDouble(double min, double limit) {
+ S2_DCHECK_LT(min, limit);
+ return min + RandDouble() * (limit - min);
+}
+
+bool S2Testing::Random::OneIn(int32 n) {
+ return Uniform(n) == 0;
+}
+
+int32 S2Testing::Random::Skewed(int max_log) {
+ S2_DCHECK_GE(max_log, 0);
+ S2_DCHECK_LE(max_log, 31);
+ int32 base = Uniform(max_log + 1);
+ return GetBits(31) & ((1U << base) - 1);
+}
+
+S2Testing::Random S2Testing::rnd;
+
+void S2Testing::AppendLoopVertices(const S2Loop& loop,
+ vector<S2Point>* vertices) {
+ int n = loop.num_vertices();
+ const S2Point* base = &loop.vertex(0);
+ S2_DCHECK_EQ(&loop.vertex(n - 1), base + n - 1);
+ vertices->insert(vertices->end(), base, base + n);
+}
+
+vector<S2Point> S2Testing::MakeRegularPoints(const S2Point& center,
+ S1Angle radius,
+ int num_vertices) {
+ unique_ptr<S2Loop> loop(
+ S2Loop::MakeRegularLoop(center, radius, num_vertices));
+ vector<S2Point> points;
+ points.reserve(loop->num_vertices());
+ for (int i = 0; i < loop->num_vertices(); i++) {
+ points.push_back(loop->vertex(i));
+ }
+ return points;
+}
+
+S1Angle S2Testing::MetersToAngle(double meters) {
+ return KmToAngle(0.001 * meters);
+}
+
+S1Angle S2Testing::KmToAngle(double km) {
+ return S1Angle::Radians(km / kEarthRadiusKm);
+}
+
+double S2Testing::AreaToMeters2(double steradians) {
+ return 1e6 * AreaToKm2(steradians);
+}
+
+double S2Testing::AreaToKm2(double steradians) {
+ return steradians * kEarthRadiusKm * kEarthRadiusKm;
+}
+
+// The overloaded Dump() function is for use within a debugger.
+void Dump(const S2Point& p) {
+ cpp_compat_cout << "S2Point: " << s2textformat::ToString(p) << std::endl;
+}
+
+void Dump(const S2Loop& loop) {
+ cpp_compat_cout << "S2Polygon: " << s2textformat::ToString(loop) << std::endl;
+}
+
+void Dump(const S2Polyline& polyline) {
+ cpp_compat_cout << "S2Polyline: " << s2textformat::ToString(polyline) << std::endl;
+}
+
+void Dump(const S2Polygon& polygon) {
+ cpp_compat_cout << "S2Polygon: " << s2textformat::ToString(polygon) << std::endl;
+}
+
+// Outputs the contents of an S2ShapeIndex in human-readable form.
+void Dump(const S2ShapeIndex& index) {
+ cpp_compat_cout << "S2ShapeIndex: " << &index << std::endl;
+ for (S2ShapeIndex::Iterator it(&index, S2ShapeIndex::BEGIN);
+ !it.done(); it.Next()) {
+ cpp_compat_cout << " id: " << it.id().ToString() << std::endl;
+ const S2ShapeIndexCell& cell = it.cell();
+ for (int s = 0; s < cell.num_clipped(); ++s) {
+ const S2ClippedShape& clipped = cell.clipped(s);
+ cpp_compat_cout << " shape_id " << clipped.shape_id() << ": ";
+ for (int e = 0; e < clipped.num_edges(); ++e) {
+ if (e > 0) cpp_compat_cout << ", ";
+ cpp_compat_cout << clipped.edge(e);
+ }
+ cpp_compat_cout << std::endl;
+ }
+ }
+}
+
+S2Point S2Testing::RandomPoint() {
+ // The order of evaluation of function arguments is unspecified,
+ // so we may not just call S2Point with three RandDouble-based args.
+ // Use temporaries to induce sequence points between calls.
+ double x = rnd.UniformDouble(-1, 1);
+ double y = rnd.UniformDouble(-1, 1);
+ double z = rnd.UniformDouble(-1, 1);
+ return S2Point(x, y, z).Normalize();
+}
+
+void S2Testing::GetRandomFrame(Vector3_d* x, Vector3_d* y, Vector3_d* z) {
+ *z = RandomPoint();
+ GetRandomFrameAt(*z, x, y);
+}
+
+Matrix3x3_d S2Testing::GetRandomFrame() {
+ return GetRandomFrameAt(RandomPoint());
+}
+
+void S2Testing::GetRandomFrameAt(const S2Point& z, S2Point* x, S2Point *y) {
+ *x = z.CrossProd(RandomPoint()).Normalize();
+ *y = z.CrossProd(*x).Normalize();
+}
+
+Matrix3x3_d S2Testing::GetRandomFrameAt(const S2Point& z) {
+ S2Point x, y;
+ GetRandomFrameAt(z, &x, &y);
+ return Matrix3x3_d::FromCols(x, y, z);
+}
+
+S2CellId S2Testing::GetRandomCellId(int level) {
+ int face = rnd.Uniform(S2CellId::kNumFaces);
+ uint64 pos = rnd.Rand64() & ((1ULL << S2CellId::kPosBits) - 1);
+ return S2CellId::FromFacePosLevel(face, pos, level);
+}
+
+S2CellId S2Testing::GetRandomCellId() {
+ return GetRandomCellId(rnd.Uniform(S2CellId::kMaxLevel + 1));
+}
+
+S2Cap S2Testing::GetRandomCap(double min_area, double max_area) {
+ double cap_area = max_area * pow(min_area / max_area, rnd.RandDouble());
+ S2_DCHECK_GE(cap_area, min_area);
+ S2_DCHECK_LE(cap_area, max_area);
+
+ // The surface area of a cap is 2*Pi times its height.
+ return S2Cap::FromCenterArea(RandomPoint(), cap_area);
+}
+
+void S2Testing::ConcentricLoopsPolygon(const S2Point& center,
+ int num_loops,
+ int num_vertices_per_loop,
+ S2Polygon* polygon) {
+ Matrix3x3_d m;
+ S2::GetFrame(center, &m);
+ vector<unique_ptr<S2Loop>> loops;
+ for (int li = 0; li < num_loops; ++li) {
+ vector<S2Point> vertices;
+ double radius = 0.005 * (li + 1) / num_loops;
+ double radian_step = 2 * M_PI / num_vertices_per_loop;
+ for (int vi = 0; vi < num_vertices_per_loop; ++vi) {
+ double angle = vi * radian_step;
+ S2Point p(radius * cos(angle), radius * sin(angle), 1);
+ vertices.push_back(S2::FromFrame(m, p.Normalize()));
+ }
+ loops.push_back(make_unique<S2Loop>(vertices));
+ }
+ polygon->InitNested(std::move(loops));
+}
+
+S2Point S2Testing::SamplePoint(const S2Cap& cap) {
+ // We consider the cap axis to be the "z" axis. We choose two other axes to
+ // complete the coordinate frame.
+
+ Matrix3x3_d m;
+ S2::GetFrame(cap.center(), &m);
+
+ // The surface area of a spherical cap is directly proportional to its
+ // height. First we choose a random height, and then we choose a random
+ // point along the circle at that height.
+
+ double h = rnd.RandDouble() * cap.height();
+ double theta = 2 * M_PI * rnd.RandDouble();
+ double r = sqrt(h * (2 - h)); // Radius of circle.
+
+ // The result should already be very close to unit-length, but we might as
+ // well make it accurate as possible.
+ return S2::FromFrame(m, S2Point(cos(theta) * r, sin(theta) * r, 1 - h))
+ .Normalize();
+}
+
+S2Point S2Testing::SamplePoint(const S2LatLngRect& rect) {
+ // First choose a latitude uniformly with respect to area on the sphere.
+ double sin_lo = sin(rect.lat().lo());
+ double sin_hi = sin(rect.lat().hi());
+ double lat = asin(rnd.UniformDouble(sin_lo, sin_hi));
+
+ // Now choose longitude uniformly within the given range.
+ double lng = rect.lng().lo() + rnd.RandDouble() * rect.lng().GetLength();
+ return S2LatLng::FromRadians(lat, lng).Normalized().ToPoint();
+}
+
+void S2Testing::CheckCovering(const S2Region& region,
+ const S2CellUnion& covering,
+ bool check_tight, S2CellId id) {
+ if (!id.is_valid()) {
+ for (int face = 0; face < 6; ++face) {
+ CheckCovering(region, covering, check_tight, S2CellId::FromFace(face));
+ }
+ return;
+ }
+
+ if (!region.MayIntersect(S2Cell(id))) {
+ // If region does not intersect id, then neither should the covering.
+ if (check_tight) S2_CHECK(!covering.Intersects(id));
+
+ } else if (!covering.Contains(id)) {
+ // The region may intersect id, but we can't assert that the covering
+ // intersects id because we may discover that the region does not actually
+ // intersect upon further subdivision. (MayIntersect is not exact.)
+ S2_CHECK(!region.Contains(S2Cell(id)));
+ S2_CHECK(!id.is_leaf());
+ S2CellId end = id.child_end();
+ S2CellId child;
+ for (child = id.child_begin(); child != end; child = child.next()) {
+ CheckCovering(region, covering, check_tight, child);
+ }
+ }
+}
+
+S2Testing::Fractal::Fractal()
+ : max_level_(-1), min_level_arg_(-1), min_level_(-1),
+ dimension_(log(4.0)/log(3.0)), /* standard Koch curve */
+ edge_fraction_(0), offset_fraction_(0) {
+ ComputeOffsets();
+}
+
+void S2Testing::Fractal::set_max_level(int max_level) {
+ S2_DCHECK_GE(max_level, 0);
+ max_level_ = max_level;
+ ComputeMinLevel();
+}
+
+void S2Testing::Fractal::set_min_level(int min_level_arg) {
+ S2_DCHECK_GE(min_level_arg, -1);
+ min_level_arg_ = min_level_arg;
+ ComputeMinLevel();
+}
+
+void S2Testing::Fractal::ComputeMinLevel() {
+ if (min_level_arg_ >= 0 && min_level_arg_ <= max_level_) {
+ min_level_ = min_level_arg_;
+ } else {
+ min_level_ = max_level_;
+ }
+}
+
+void S2Testing::Fractal::set_fractal_dimension(double dimension) {
+ S2_DCHECK_GE(dimension, 1.0);
+ S2_DCHECK_LT(dimension, 2.0);
+ dimension_ = dimension;
+ ComputeOffsets();
+}
+
+void S2Testing::Fractal::ComputeOffsets() {
+ edge_fraction_ = pow(4.0, -1.0 / dimension_);
+ offset_fraction_ = sqrt(edge_fraction_ - 0.25);
+}
+
+void S2Testing::Fractal::SetLevelForApproxMinEdges(int min_edges) {
+ // Map values in the range [3*(4**n)/2, 3*(4**n)*2) to level n.
+ set_min_level(round(0.5 * log2(min_edges / 3)));
+}
+
+void S2Testing::Fractal::SetLevelForApproxMaxEdges(int max_edges) {
+ // Map values in the range [3*(4**n)/2, 3*(4**n)*2) to level n.
+ set_max_level(round(0.5 * log2(max_edges / 3)));
+}
+
+double S2Testing::Fractal::min_radius_factor() const {
+ // The minimum radius is attained at one of the vertices created by the
+ // first subdivision step as long as the dimension is not too small (at
+ // least kMinDimensionForMinRadiusAtLevel1, see below). Otherwise we fall
+ // back on the incircle radius of the original triangle, which is always a
+ // lower bound (and is attained when dimension = 1).
+ //
+ // The value below was obtained by letting AE be an original triangle edge,
+ // letting ABCDE be the corresponding polyline after one subdivision step,
+ // and then letting BC be tangent to the inscribed circle at the center of
+ // the fractal O. This gives rise to a pair of similar triangles whose edge
+ // length ratios can be used to solve for the corresponding "edge fraction".
+ // This method is slightly conservative because it is computed using planar
+ // rather than spherical geometry. The value below is equal to
+ // -log(4)/log((2 + cbrt(2) - cbrt(4))/6).
+ const double kMinDimensionForMinRadiusAtLevel1 = 1.0852230903040407;
+ if (dimension_ >= kMinDimensionForMinRadiusAtLevel1) {
+ return sqrt(1 + 3 * edge_fraction_ * (edge_fraction_ - 1));
+ }
+ return 0.5;
+}
+
+double S2Testing::Fractal::max_radius_factor() const {
+ // The maximum radius is always attained at either an original triangle
+ // vertex or at a middle vertex from the first subdivision step.
+ return max(1.0, offset_fraction_ * sqrt(3.0) + 0.5);
+}
+
+void S2Testing::Fractal::GetR2Vertices(vector<R2Point>* vertices) const {
+ // The Koch "snowflake" consists of three Koch curves whose initial edges
+ // form an equilateral triangle.
+ R2Point v0(1.0, 0.0);
+ R2Point v1(-0.5, sqrt(3.0)/2);
+ R2Point v2(-0.5, -sqrt(3.0)/2);
+ GetR2VerticesHelper(v0, v1, 0, vertices);
+ GetR2VerticesHelper(v1, v2, 0, vertices);
+ GetR2VerticesHelper(v2, v0, 0, vertices);
+}
+
+// Given the two endpoints (v0,v4) of an edge, recursively subdivide the edge
+// to the desired level, and insert all vertices of the resulting curve up to
+// but not including the endpoint "v4".
+void S2Testing::Fractal::GetR2VerticesHelper(const R2Point& v0,
+ const R2Point& v4, int level,
+ vector<R2Point>* vertices) const {
+ if (level >= min_level_ && S2Testing::rnd.OneIn(max_level_ - level + 1)) {
+ // Stop subdivision at this level.
+ vertices->push_back(v0);
+ return;
+ }
+ // Otherwise compute the intermediate vertices v1, v2, and v3.
+ Vector2_d dir = v4 - v0;
+ R2Point v1 = v0 + edge_fraction_ * dir;
+ R2Point v2 = 0.5 * (v0 + v4) - offset_fraction_ * dir.Ortho();
+ R2Point v3 = v4 - edge_fraction_ * dir;
+
+ // And recurse on the four sub-edges.
+ GetR2VerticesHelper(v0, v1, level+1, vertices);
+ GetR2VerticesHelper(v1, v2, level+1, vertices);
+ GetR2VerticesHelper(v2, v3, level+1, vertices);
+ GetR2VerticesHelper(v3, v4, level+1, vertices);
+}
+
+std::unique_ptr<S2Loop> S2Testing::Fractal::MakeLoop(
+ const Matrix3x3_d& frame,
+ S1Angle nominal_radius) const {
+ vector<R2Point> r2vertices;
+ GetR2Vertices(&r2vertices);
+ vector<S2Point> vertices;
+ double r = nominal_radius.radians();
+ for (const R2Point& v : r2vertices) {
+ S2Point p(v[0] * r, v[1] * r, 1);
+ vertices.push_back(S2::FromFrame(frame, p).Normalize());
+ }
+ return make_unique<S2Loop>(vertices);
+}
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "s2/s2text_format.h"
+
+#include <string>
+#include <vector>
+
+#include "s2/base/logging.h"
+#include "s2/base/stringprintf.h"
+#include "s2/strings/serialize.h"
+#include "s2/third_party/absl/memory/memory.h"
+#include "s2/third_party/absl/strings/str_split.h"
+#include "s2/third_party/absl/strings/string_view.h"
+#include "s2/third_party/absl/strings/strip.h"
+#include "s2/mutable_s2shape_index.h"
+#include "s2/s2latlng.h"
+#include "s2/s2lax_polygon_shape.h"
+#include "s2/s2lax_polyline_shape.h"
+#include "s2/s2loop.h"
+#include "s2/s2point_vector_shape.h"
+#include "s2/s2polygon.h"
+#include "s2/s2polyline.h"
+
+using absl::make_unique;
+using absl::string_view;
+using std::pair;
+using std::unique_ptr;
+using std::vector;
+
+namespace s2textformat {
+
+static vector<string_view> SplitString(string_view str, char separator) {
+ vector<string_view> result =
+ absl::StrSplit(str, separator, absl::SkipWhitespace());
+ for (auto& e : result) {
+ e = absl::StripAsciiWhitespace(e);
+ }
+ return result;
+}
+
+static bool ParseDouble(const string& str, double* value) {
+ char* end_ptr = nullptr;
+ *value = strtod(str.c_str(), &end_ptr);
+ return end_ptr && *end_ptr == 0;
+}
+
+vector<S2LatLng> ParseLatLngsOrDie(string_view str) {
+ vector<S2LatLng> latlngs;
+ S2_CHECK(ParseLatLngs(str, &latlngs)) << ": str == \"" << str << "\"";
+ return latlngs;
+}
+
+bool ParseLatLngs(string_view str, vector<S2LatLng>* latlngs) {
+ vector<pair<string, string>> ps;
+ if (!strings::DictionaryParse(str, &ps)) return false;
+ for (const auto& p : ps) {
+ double lat;
+ if (!ParseDouble(p.first, &lat)) return false;
+ double lng;
+ if (!ParseDouble(p.second, &lng)) return false;
+ latlngs->push_back(S2LatLng::FromDegrees(lat, lng));
+ }
+ return true;
+}
+
+vector<S2Point> ParsePointsOrDie(string_view str) {
+ vector<S2Point> vertices;
+ S2_CHECK(ParsePoints(str, &vertices)) << ": str == \"" << str << "\"";
+ return vertices;
+}
+
+bool ParsePoints(string_view str, vector<S2Point>* vertices) {
+ vector<S2LatLng> latlngs;
+ if (!ParseLatLngs(str, &latlngs)) return false;
+ for (const auto& latlng : latlngs) {
+ vertices->push_back(latlng.ToPoint());
+ }
+ return true;
+}
+
+S2Point MakePointOrDie(string_view str) {
+ S2Point point;
+ S2_CHECK(MakePoint(str, &point)) << ": str == \"" << str << "\"";
+ return point;
+}
+
+bool MakePoint(string_view str, S2Point* point) {
+ vector<S2Point> vertices;
+ if (!ParsePoints(str, &vertices) || vertices.size() != 1) return false;
+ *point = vertices[0];
+ return true;
+}
+
+bool MakeLatLng(string_view str, S2LatLng* latlng) {
+ std::vector<S2LatLng> latlngs;
+ if (!ParseLatLngs(str, &latlngs) || latlngs.size() != 1) return false;
+ *latlng = latlngs[0];
+ return true;
+}
+
+S2LatLng MakeLatLngOrDie(string_view str) {
+ S2LatLng latlng;
+ S2_CHECK(MakeLatLng(str, &latlng)) << ": str == \"" << str << "\"";
+ return latlng;
+}
+
+S2LatLngRect MakeLatLngRectOrDie(string_view str) {
+ S2LatLngRect rect;
+ S2_CHECK(MakeLatLngRect(str, &rect)) << ": str == \"" << str << "\"";
+ return rect;
+}
+
+bool MakeLatLngRect(string_view str, S2LatLngRect* rect) {
+ vector<S2LatLng> latlngs;
+ if (!ParseLatLngs(str, &latlngs) || latlngs.empty()) return false;
+ *rect = S2LatLngRect::FromPoint(latlngs[0]);
+ for (int i = 1; i < latlngs.size(); ++i) {
+ rect->AddPoint(latlngs[i]);
+ }
+ return rect;
+}
+
+bool MakeCellId(string_view str, S2CellId* cell_id) {
+ *cell_id = S2CellId::FromDebugString(str);
+ return *cell_id != S2CellId::None();
+}
+
+S2CellId MakeCellIdOrDie(string_view str) {
+ S2CellId cell_id;
+ S2_CHECK(MakeCellId(str, &cell_id)) << ": str == \"" << str << "\"";
+ return cell_id;
+}
+
+bool MakeCellUnion(string_view str, S2CellUnion* cell_union) {
+ vector<S2CellId> cell_ids;
+ for (const auto& cell_str : SplitString(str, ',')) {
+ S2CellId cell_id;
+ if (!MakeCellId(cell_str, &cell_id)) return false;
+ cell_ids.push_back(cell_id);
+ }
+ *cell_union = S2CellUnion(std::move(cell_ids));
+ return true;
+}
+
+S2CellUnion MakeCellUnionOrDie(string_view str) {
+ S2CellUnion cell_union;
+ S2_CHECK(MakeCellUnion(str, &cell_union)) << ": str == \"" << str << "\"";
+ return cell_union;
+}
+
+unique_ptr<S2Loop> MakeLoopOrDie(string_view str, S2Debug debug_override) {
+ unique_ptr<S2Loop> loop;
+ S2_CHECK(MakeLoop(str, &loop, debug_override)) << ": str == \"" << str << "\"";
+ return loop;
+}
+
+bool MakeLoop(string_view str, unique_ptr<S2Loop>* loop,
+ S2Debug debug_override) {
+ if (str == "empty") {
+ *loop = make_unique<S2Loop>(S2Loop::kEmpty());
+ return true;
+ }
+ if (str == "full") {
+ *loop = make_unique<S2Loop>(S2Loop::kFull());
+ return true;
+ }
+ vector<S2Point> vertices;
+ if (!ParsePoints(str, &vertices)) return false;
+ *loop = make_unique<S2Loop>(vertices, debug_override);
+ return true;
+}
+
+std::unique_ptr<S2Loop> MakeLoop(string_view str, S2Debug debug_override) {
+ return MakeLoopOrDie(str, debug_override);
+}
+
+unique_ptr<S2Polyline> MakePolylineOrDie(string_view str,
+ S2Debug debug_override) {
+ unique_ptr<S2Polyline> polyline;
+ S2_CHECK(MakePolyline(str, &polyline, debug_override))
+ << ": str == \"" << str << "\"";
+ return polyline;
+}
+
+bool MakePolyline(string_view str, unique_ptr<S2Polyline>* polyline,
+ S2Debug debug_override) {
+ vector<S2Point> vertices;
+ if (!ParsePoints(str, &vertices)) return false;
+ *polyline = make_unique<S2Polyline>(vertices, debug_override);
+ return true;
+}
+
+std::unique_ptr<S2Polyline> MakePolyline(string_view str,
+ S2Debug debug_override) {
+ return MakePolylineOrDie(str, debug_override);
+}
+
+unique_ptr<S2LaxPolylineShape> MakeLaxPolylineOrDie(string_view str) {
+ unique_ptr<S2LaxPolylineShape> lax_polyline;
+ S2_CHECK(MakeLaxPolyline(str, &lax_polyline)) << ": str == \"" << str << "\"";
+ return lax_polyline;
+}
+
+bool MakeLaxPolyline(string_view str,
+ unique_ptr<S2LaxPolylineShape>* lax_polyline) {
+ vector<S2Point> vertices;
+ if (!ParsePoints(str, &vertices)) return false;
+ *lax_polyline = make_unique<S2LaxPolylineShape>(vertices);
+ return true;
+}
+
+std::unique_ptr<S2LaxPolylineShape> MakeLaxPolyline(string_view str) {
+ return MakeLaxPolylineOrDie(str);
+}
+
+static bool InternalMakePolygon(string_view str,
+ S2Debug debug_override,
+ bool normalize_loops,
+ unique_ptr<S2Polygon>* polygon) {
+ if (str == "empty") str = "";
+ vector<string_view> loop_strs = SplitString(str, ';');
+ vector<unique_ptr<S2Loop>> loops;
+ for (const auto& loop_str : loop_strs) {
+ std::unique_ptr<S2Loop> loop;
+ if (!MakeLoop(loop_str, &loop, debug_override)) return false;
+ // Don't normalize loops that were explicitly specified as "full".
+ if (normalize_loops && !loop->is_full()) loop->Normalize();
+ loops.push_back(std::move(loop));
+ }
+ *polygon = make_unique<S2Polygon>(std::move(loops), debug_override);
+ return true;
+}
+
+unique_ptr<S2Polygon> MakePolygonOrDie(string_view str,
+ S2Debug debug_override) {
+ unique_ptr<S2Polygon> polygon;
+ S2_CHECK(MakePolygon(str, &polygon, debug_override))
+ << ": str == \"" << str << "\"";
+ return polygon;
+}
+
+bool MakePolygon(string_view str, unique_ptr<S2Polygon>* polygon,
+ S2Debug debug_override) {
+ return InternalMakePolygon(str, debug_override, true, polygon);
+}
+
+std::unique_ptr<S2Polygon> MakePolygon(string_view str,
+ S2Debug debug_override) {
+ return MakePolygonOrDie(str, debug_override);
+}
+
+unique_ptr<S2Polygon> MakeVerbatimPolygonOrDie(string_view str) {
+ unique_ptr<S2Polygon> polygon;
+ S2_CHECK(MakeVerbatimPolygon(str, &polygon)) << ": str == \"" << str << "\"";
+ return polygon;
+}
+
+bool MakeVerbatimPolygon(string_view str, unique_ptr<S2Polygon>* polygon) {
+ return InternalMakePolygon(str, S2Debug::ALLOW, false, polygon);
+}
+
+std::unique_ptr<S2Polygon> MakeVerbatimPolygon(string_view str) {
+ return MakeVerbatimPolygonOrDie(str);
+}
+
+unique_ptr<S2LaxPolygonShape> MakeLaxPolygonOrDie(string_view str) {
+ unique_ptr<S2LaxPolygonShape> lax_polygon;
+ S2_CHECK(MakeLaxPolygon(str, &lax_polygon)) << ": str == \"" << str << "\"";
+ return lax_polygon;
+}
+
+bool MakeLaxPolygon(string_view str,
+ unique_ptr<S2LaxPolygonShape>* lax_polygon) {
+ vector<string_view> loop_strs = SplitString(str, ';');
+ vector<vector<S2Point>> loops;
+ for (const auto& loop_str : loop_strs) {
+ if (loop_str == "full") {
+ loops.push_back(vector<S2Point>());
+ } else if (loop_str != "empty") {
+ vector<S2Point> points;
+ if (!ParsePoints(loop_str, &points)) return false;
+ loops.push_back(points);
+ }
+ }
+ *lax_polygon = make_unique<S2LaxPolygonShape>(loops);
+ return true;
+}
+
+std::unique_ptr<S2LaxPolygonShape> MakeLaxPolygon(string_view str) {
+ return MakeLaxPolygonOrDie(str);
+}
+
+unique_ptr<MutableS2ShapeIndex> MakeIndexOrDie(string_view str) {
+ auto index = make_unique<MutableS2ShapeIndex>();
+ S2_CHECK(MakeIndex(str, &index)) << ": str == \"" << str << "\"";
+ return index;
+}
+
+bool MakeIndex(string_view str, std::unique_ptr<MutableS2ShapeIndex>* index) {
+ vector<string_view> strs = absl::StrSplit(str, '#');
+ S2_DCHECK_EQ(3, strs.size()) << "Must contain two # characters: " << str;
+
+ vector<S2Point> points;
+ for (const auto& point_str : SplitString(strs[0], '|')) {
+ S2Point point;
+ if (!MakePoint(point_str, &point)) return false;
+ points.push_back(point);
+ }
+ if (!points.empty()) {
+ (*index)->Add(make_unique<S2PointVectorShape>(std::move(points)));
+ }
+ for (const auto& line_str : SplitString(strs[1], '|')) {
+ std::unique_ptr<S2LaxPolylineShape> lax_polyline;
+ if (!MakeLaxPolyline(line_str, &lax_polyline)) return false;
+ (*index)->Add(unique_ptr<S2Shape>(lax_polyline.release()));
+ }
+ for (const auto& polygon_str : SplitString(strs[2], '|')) {
+ std::unique_ptr<S2LaxPolygonShape> lax_polygon;
+ if (!MakeLaxPolygon(polygon_str, &lax_polygon)) return false;
+ (*index)->Add(unique_ptr<S2Shape>(lax_polygon.release()));
+ }
+ return true;
+}
+
+std::unique_ptr<MutableS2ShapeIndex> MakeIndex(string_view str) {
+ return MakeIndexOrDie(str);
+}
+
+static void AppendVertex(const S2LatLng& ll, string* out) {
+ StringAppendF(out, "%.15g:%.15g", ll.lat().degrees(), ll.lng().degrees());
+}
+
+static void AppendVertex(const S2Point& p, string* out) {
+ S2LatLng ll(p);
+ return AppendVertex(ll, out);
+}
+
+static void AppendVertices(const S2Point* v, int n, string* out) {
+ for (int i = 0; i < n; ++i) {
+ if (i > 0) *out += ", ";
+ AppendVertex(v[i], out);
+ }
+}
+
+string ToString(const S2Point& point) {
+ string out;
+ AppendVertex(point, &out);
+ return out;
+}
+
+string ToString(const S2LatLng& latlng) {
+ string out;
+ AppendVertex(latlng, &out);
+ return out;
+}
+
+string ToString(const S2LatLngRect& rect) {
+ string out;
+ AppendVertex(rect.lo(), &out);
+ out += ", ";
+ AppendVertex(rect.hi(), &out);
+ return out;
+}
+
+string ToString(const S2CellId& cell_id) {
+ return cell_id.ToString();
+}
+
+string ToString(const S2CellUnion& cell_union) {
+ string out;
+ for (S2CellId cell_id : cell_union) {
+ if (!out.empty()) out += ", ";
+ out += cell_id.ToString();
+ }
+ return out;
+}
+
+string ToString(const S2Loop& loop) {
+ if (loop.is_empty()) {
+ return "empty";
+ } else if (loop.is_full()) {
+ return "full";
+ }
+ string out;
+ if (loop.num_vertices() > 0) {
+ AppendVertices(&loop.vertex(0), loop.num_vertices(), &out);
+ }
+ return out;
+}
+
+string ToString(S2PointLoopSpan loop) {
+ // S2Shape represents the full loop as a loop with no vertices.
+ // There is no representation of the empty loop.
+ if (loop.empty()) {
+ return "full";
+ }
+ string out;
+ AppendVertices(loop.data(), loop.size(), &out);
+ return out;
+}
+
+string ToString(const S2Polyline& polyline) {
+ string out;
+ if (polyline.num_vertices() > 0) {
+ AppendVertices(&polyline.vertex(0), polyline.num_vertices(), &out);
+ }
+ return out;
+}
+
+string ToString(const S2Polygon& polygon, const char* loop_separator) {
+ if (polygon.is_empty()) {
+ return "empty";
+ } else if (polygon.is_full()) {
+ return "full";
+ }
+ string out;
+ for (int i = 0; i < polygon.num_loops(); ++i) {
+ if (i > 0) out += loop_separator;
+ const S2Loop& loop = *polygon.loop(i);
+ AppendVertices(&loop.vertex(0), loop.num_vertices(), &out);
+ }
+ return out;
+}
+
+string ToString(const vector<S2Point>& points) {
+ string out;
+ AppendVertices(points.data(), points.size(), &out);
+ return out;
+}
+
+string ToString(const vector<S2LatLng>& latlngs) {
+ string out;
+ for (int i = 0; i < latlngs.size(); ++i) {
+ if (i > 0) out += ", ";
+ AppendVertex(latlngs[i], &out);
+ }
+ return out;
+}
+
+string ToString(const S2LaxPolylineShape& polyline) {
+ string out;
+ if (polyline.num_vertices() > 0) {
+ AppendVertices(&polyline.vertex(0), polyline.num_vertices(), &out);
+ }
+ return out;
+}
+
+string ToString(const S2LaxPolygonShape& polygon, const char* loop_separator) {
+ string out;
+ for (int i = 0; i < polygon.num_loops(); ++i) {
+ if (i > 0) out += loop_separator;
+ int n = polygon.num_loop_vertices(i);
+ if (n == 0) {
+ out += "full";
+ } else {
+ AppendVertices(&polygon.loop_vertex(i, 0), n, &out);
+ }
+ }
+ return out;
+}
+
+string ToString(const S2ShapeIndex& index) {
+ string out;
+ for (int dim = 0; dim < 3; ++dim) {
+ if (dim > 0) out += "#";
+ int count = 0;
+ for (S2Shape* shape : index) {
+ if (shape == nullptr || shape->dimension() != dim) continue;
+ out += (count > 0) ? " | " : (dim > 0) ? " " : "";
+ for (int i = 0; i < shape->num_chains(); ++i, ++count) {
+ if (i > 0) out += (dim == 2) ? "; " : " | ";
+ S2Shape::Chain chain = shape->chain(i);
+ if (chain.length == 0) {
+ S2_DCHECK_EQ(dim, 2);
+ out += "full";
+ } else {
+ AppendVertex(shape->edge(chain.start).v0, &out);
+ }
+ int limit = chain.start + chain.length;
+ if (dim != 1) --limit;
+ for (int e = chain.start; e < limit; ++e) {
+ out += ", ";
+ AppendVertex(shape->edge(e).v1, &out);
+ }
+ }
+ }
+ // Example output: "# #", "0:0 # #", "# # 0:0, 0:1, 1:0"
+ if (dim == 1 || (dim == 0 && count > 0)) out += " ";
+ }
+ return out;
+}
+
+} // namespace s2textformat
--- /dev/null
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2wedge_relations.h"
+
+#include "s2/s2predicates.h"
+
+namespace S2 {
+
+bool WedgeContains(
+ const S2Point& a0, const S2Point& ab1, const S2Point& a2,
+ const S2Point& b0, const S2Point& b2) {
+ // For A to contain B (where each loop interior is defined to be its left
+ // side), the CCW edge order around ab1 must be a2 b2 b0 a0. We split
+ // this test into two parts that test three vertices each.
+ return (s2pred::OrderedCCW(a2, b2, b0, ab1) &&
+ s2pred::OrderedCCW(b0, a0, a2, ab1));
+}
+
+bool WedgeIntersects(
+ const S2Point& a0, const S2Point& ab1, const S2Point& a2,
+ const S2Point& b0, const S2Point& b2) {
+ // For A not to intersect B (where each loop interior is defined to be
+ // its left side), the CCW edge order around ab1 must be a0 b2 b0 a2.
+ // Note that it's important to write these conditions as negatives
+ // (!OrderedCCW(a,b,c,o) rather than Ordered(c,b,a,o)) to get correct
+ // results when two vertices are the same.
+ return !(s2pred::OrderedCCW(a0, b2, b0, ab1) &&
+ s2pred::OrderedCCW(b0, a2, a0, ab1));
+}
+
+WedgeRelation GetWedgeRelation(
+ const S2Point& a0, const S2Point& ab1, const S2Point& a2,
+ const S2Point& b0, const S2Point& b2) {
+ // There are 6 possible edge orderings at a shared vertex (all
+ // of these orderings are circular, i.e. abcd == bcda):
+ //
+ // (1) a2 b2 b0 a0: A contains B
+ // (2) a2 a0 b0 b2: B contains A
+ // (3) a2 a0 b2 b0: A and B are disjoint
+ // (4) a2 b0 a0 b2: A and B intersect in one wedge
+ // (5) a2 b2 a0 b0: A and B intersect in one wedge
+ // (6) a2 b0 b2 a0: A and B intersect in two wedges
+ //
+ // We do not distinguish between 4, 5, and 6.
+ // We pay extra attention when some of the edges overlap. When edges
+ // overlap, several of these orderings can be satisfied, and we take
+ // the most specific.
+ if (a0 == b0 && a2 == b2) return WEDGE_EQUALS;
+
+ if (s2pred::OrderedCCW(a0, a2, b2, ab1)) {
+ // The cases with this vertex ordering are 1, 5, and 6,
+ // although case 2 is also possible if a2 == b2.
+ if (s2pred::OrderedCCW(b2, b0, a0, ab1)) return WEDGE_PROPERLY_CONTAINS;
+
+ // We are in case 5 or 6, or case 2 if a2 == b2.
+ return (a2 == b2) ? WEDGE_IS_PROPERLY_CONTAINED : WEDGE_PROPERLY_OVERLAPS;
+ }
+
+ // We are in case 2, 3, or 4.
+ if (s2pred::OrderedCCW(a0, b0, b2, ab1)) return WEDGE_IS_PROPERLY_CONTAINED;
+ return s2pred::OrderedCCW(a0, b0, a2, ab1) ?
+ WEDGE_IS_DISJOINT : WEDGE_PROPERLY_OVERLAPS;
+}
+
+} // namespace S2
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <cassert>
+
+#include "s2/strings/ostringstream.h"
+
+namespace strings {
+
+OStringStream::Buf::int_type OStringStream::overflow(int c) {
+ assert(s_);
+ if (!Buf::traits_type::eq_int_type(c, Buf::traits_type::eof()))
+ s_->push_back(static_cast<char>(c));
+ return 1;
+}
+
+std::streamsize OStringStream::xsputn(const char* s, std::streamsize n) {
+ assert(s_);
+ s_->append(s, n);
+ return n;
+}
+
+} // namespace strings
--- /dev/null
+// Copyright Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "s2/strings/serialize.h"
+
+#include <string>
+#include <vector>
+
+#include "s2/third_party/absl/strings/str_split.h"
+#include "s2/third_party/absl/strings/string_view.h"
+
+using absl::StrSplit;
+using absl::string_view;
+using std::pair;
+using std::string;
+using std::vector;
+
+namespace strings {
+
+bool DictionaryParse(string_view encoded_str,
+ vector<pair<string, string>>* items) {
+ if (encoded_str.empty())
+ return true;
+ vector<string_view> const entries = StrSplit(encoded_str, ',');
+ for (int i = 0; i < entries.size(); ++i) {
+ vector<string_view> const fields = StrSplit(entries[i], ':');
+ if (fields.size() != 2) // parsing error
+ return false;
+ items->push_back(std::make_pair(string(fields[0]), string(fields[1])));
+ }
+ return true;
+}
+
+} // namespace strings
--- /dev/null
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstdlib>
+#include <cstring>
+
+#include "s2/third_party/absl/base/dynamic_annotations.h"
+
+#ifndef __has_feature
+#define __has_feature(x) 0
+#endif
+
+/* Compiler-based ThreadSanitizer defines
+ DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL = 1
+ and provides its own definitions of the functions. */
+
+#ifndef DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL
+# define DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL 0
+#endif
+
+/* Each function is empty and called (via a macro) only in debug mode.
+ The arguments are captured by dynamic tools at runtime. */
+
+#if DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL == 0 && !defined(__native_client__)
+
+#if __has_feature(memory_sanitizer)
+#include <sanitizer/msan_interface.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void AnnotateRWLockCreate(const char *, int,
+ const volatile void *){}
+void AnnotateRWLockDestroy(const char *, int,
+ const volatile void *){}
+void AnnotateRWLockAcquired(const char *, int,
+ const volatile void *, long){}
+void AnnotateRWLockReleased(const char *, int,
+ const volatile void *, long){}
+void AnnotateBenignRace(const char *, int,
+ const volatile void *,
+ const char *){}
+void AnnotateBenignRaceSized(const char *, int,
+ const volatile void *,
+ size_t,
+ const char *) {}
+void AnnotateThreadName(const char *, int,
+ const char *){}
+void AnnotateIgnoreReadsBegin(const char *, int){}
+void AnnotateIgnoreReadsEnd(const char *, int){}
+void AnnotateIgnoreWritesBegin(const char *, int){}
+void AnnotateIgnoreWritesEnd(const char *, int){}
+void AnnotateEnableRaceDetection(const char *, int, int){}
+void AnnotateMemoryIsInitialized(const char *, int,
+ const volatile void *mem, size_t size) {
+#if __has_feature(memory_sanitizer)
+ __msan_unpoison(mem, size);
+#else
+ (void)mem;
+ (void)size;
+#endif
+}
+
+void AnnotateMemoryIsUninitialized(const char *, int,
+ const volatile void *mem, size_t size) {
+#if __has_feature(memory_sanitizer)
+ __msan_allocated_memory(mem, size);
+#else
+ (void)mem;
+ (void)size;
+#endif
+}
+
+static int GetRunningOnValgrind(void) {
+#ifdef RUNNING_ON_VALGRIND
+ if (RUNNING_ON_VALGRIND) return 1;
+#endif
+ char *running_on_valgrind_str = getenv("RUNNING_ON_VALGRIND");
+ if (running_on_valgrind_str) {
+ return strcmp(running_on_valgrind_str, "0") != 0;
+ }
+ return 0;
+}
+
+/* See the comments in dynamic_annotations.h */
+int RunningOnValgrind(void) {
+ static volatile int running_on_valgrind = -1;
+ int local_running_on_valgrind = running_on_valgrind;
+ /* C doesn't have thread-safe initialization of statics, and we
+ don't want to depend on pthread_once here, so hack it. */
+ ANNOTATE_BENIGN_RACE(&running_on_valgrind, "safe hack");
+ if (local_running_on_valgrind == -1)
+ running_on_valgrind = local_running_on_valgrind = GetRunningOnValgrind();
+ return local_running_on_valgrind;
+}
+
+/* See the comments in dynamic_annotations.h */
+double ValgrindSlowdown(void) {
+ /* Same initialization hack as in RunningOnValgrind(). */
+ static volatile double slowdown = 0.0;
+ double local_slowdown = slowdown;
+ ANNOTATE_BENIGN_RACE(&slowdown, "safe hack");
+ if (RunningOnValgrind() == 0) {
+ return 1.0;
+ }
+ if (local_slowdown == 0.0) {
+ char *env = getenv("VALGRIND_SLOWDOWN");
+ slowdown = local_slowdown = env ? atof(env) : 50.0;
+ }
+ return local_slowdown;
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+#endif /* DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL == 0 */
--- /dev/null
+#include "cpp-compat.h"
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "s2/third_party/absl/base/internal/raw_logging.h"
+
+#include <cstddef>
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "s2/third_party/absl/base/attributes.h"
+#include "s2/third_party/absl/base/config.h"
+#include "s2/third_party/absl/base/internal/atomic_hook.h"
+#include "s2/third_party/absl/base/log_severity.h"
+
+// We know how to perform low-level writes to stderr in POSIX and Windows. For
+// these platforms, we define the token ABSL_LOW_LEVEL_WRITE_SUPPORTED.
+// Much of raw_logging.cc becomes a no-op when we can't output messages,
+// although a FATAL ABSL_RAW_LOG message will still abort the process.
+
+// ABSL_HAVE_POSIX_WRITE is defined when the platform provides posix write()
+// (as from unistd.h)
+//
+// This preprocessor token is also defined in raw_io.cc. If you need to copy
+// this, consider moving both to config.h instead.
+#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
+ defined(__Fuchsia__) || defined(__native_client__) || defined(__ASYLO__)
+#include <unistd.h>
+
+
+#define ABSL_HAVE_POSIX_WRITE 1
+#define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1
+#else
+#undef ABSL_HAVE_POSIX_WRITE
+#endif
+
+// ABSL_HAVE_SYSCALL_WRITE is defined when the platform provides the syscall
+// syscall(SYS_write, /*int*/ fd, /*char* */ buf, /*size_t*/ len);
+// for low level operations that want to avoid libc.
+#if (defined(__linux__) || defined(__FreeBSD__)) && !defined(__ANDROID__)
+#include <sys/syscall.h>
+#define ABSL_HAVE_SYSCALL_WRITE 1
+#define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1
+#else
+#undef ABSL_HAVE_SYSCALL_WRITE
+#endif
+
+#ifdef _WIN32
+#include <io.h>
+
+#define ABSL_HAVE_RAW_IO 1
+#define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1
+#else
+#undef ABSL_HAVE_RAW_IO
+#endif
+
+// TODO(user): We want raw-logging to work on as many platforms as possible.
+// Explicitly #error out when not ABSL_LOW_LEVEL_WRITE_SUPPORTED, except for a
+// whitelisted set of platforms for which we expect not to be able to raw log.
+
+ABSL_CONST_INIT static absl::base_internal::AtomicHook<
+ absl::raw_logging_internal::LogPrefixHook> log_prefix_hook;
+ABSL_CONST_INIT static absl::base_internal::AtomicHook<
+ absl::raw_logging_internal::AbortHook> abort_hook;
+
+#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
+static const char kTruncated[] = " ... (message truncated)\n";
+
+// sprintf the format to the buffer, adjusting *buf and *size to reflect the
+// consumed bytes, and return whether the message fit without truncation. If
+// truncation occurred, if possible leave room in the buffer for the message
+// kTruncated[].
+inline static bool VADoRawLog(char** buf, int* size, const char* format,
+ va_list ap) ABSL_PRINTF_ATTRIBUTE(3, 0);
+inline static bool VADoRawLog(char** buf, int* size,
+ const char* format, va_list ap) {
+ int n = vsnprintf(*buf, *size, format, ap);
+ bool result = true;
+ if (n < 0 || n > *size) {
+ result = false;
+ if (static_cast<size_t>(*size) > sizeof(kTruncated)) {
+ n = *size - sizeof(kTruncated); // room for truncation message
+ } else {
+ n = 0; // no room for truncation message
+ }
+ }
+ *size -= n;
+ *buf += n;
+ return result;
+}
+#endif // ABSL_LOW_LEVEL_WRITE_SUPPORTED
+
+static constexpr int kLogBufSize = 3000;
+
+namespace {
+
+// CAVEAT: vsnprintf called from *DoRawLog below has some (exotic) code paths
+// that invoke malloc() and getenv() that might acquire some locks.
+// If this becomes a problem we should reimplement a subset of vsnprintf
+// that does not need locks and malloc.
+// E.g. //third_party/clearsilver/core/util/snprintf.c
+// looks like such a reimplementation.
+
+// Helper for RawLog below.
+// *DoRawLog writes to *buf of *size and move them past the written portion.
+// It returns true iff there was no overflow or error.
+bool DoRawLog(char** buf, int* size, const char* format, ...)
+ ABSL_PRINTF_ATTRIBUTE(3, 4);
+bool DoRawLog(char** buf, int* size, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ int n = vsnprintf(*buf, *size, format, ap);
+ va_end(ap);
+ if (n < 0 || n > *size) return false;
+ *size -= n;
+ *buf += n;
+ return true;
+}
+
+void RawLogVA(absl::LogSeverity severity, const char* file, int line,
+ const char* format, va_list ap) ABSL_PRINTF_ATTRIBUTE(4, 0);
+void RawLogVA(absl::LogSeverity severity, const char* file, int line,
+ const char* format, va_list ap) {
+ char buffer[kLogBufSize];
+ char* buf = buffer;
+ int size = sizeof(buffer);
+#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
+ bool enabled = true;
+#else
+ bool enabled = false;
+#endif
+
+#ifdef ABSL_MIN_LOG_LEVEL
+ if (static_cast<int>(severity) < ABSL_MIN_LOG_LEVEL &&
+ severity < absl::LogSeverity::kFatal) {
+ enabled = false;
+ }
+#endif
+
+ auto log_prefix_hook_ptr = log_prefix_hook.Load();
+ if (log_prefix_hook_ptr) {
+ enabled = log_prefix_hook_ptr(severity, file, line, &buf, &size);
+ } else {
+ if (enabled) {
+ DoRawLog(&buf, &size, "[%s : %d] RAW: ", file, line);
+ }
+ }
+ const char* const prefix_end = buf;
+
+#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
+ if (enabled) {
+ bool no_chop = VADoRawLog(&buf, &size, format, ap);
+ if (no_chop) {
+ DoRawLog(&buf, &size, "\n");
+ } else {
+ DoRawLog(&buf, &size, "%s", kTruncated);
+ }
+ absl::raw_logging_internal::SafeWriteToStderr(buffer, strlen(buffer));
+ }
+#else
+ static_cast<void>(format);
+ static_cast<void>(ap);
+#endif
+
+ // Abort the process after logging a FATAL message, even if the output itself
+ // was suppressed.
+ if (severity == absl::LogSeverity::kFatal) {
+ abort_hook(file, line, buffer, prefix_end, buffer + kLogBufSize);
+ cpp_compat_abort();
+ }
+}
+
+} // namespace
+
+namespace absl {
+namespace raw_logging_internal {
+void SafeWriteToStderr(const char *s, size_t len) {
+#if defined(ABSL_HAVE_SYSCALL_WRITE)
+ syscall(SYS_write, STDERR_FILENO, s, len);
+#elif defined(ABSL_HAVE_POSIX_WRITE)
+ write(STDERR_FILENO, s, len);
+#elif defined(ABSL_HAVE_RAW_IO)
+ _write(/* stderr */ 2, s, len);
+#else
+ // stderr logging unsupported on this platform
+ (void) s;
+ (void) len;
+#endif
+}
+
+void RawLog(absl::LogSeverity severity, const char* file, int line,
+ const char* format, ...) ABSL_PRINTF_ATTRIBUTE(4, 5);
+void RawLog(absl::LogSeverity severity, const char* file, int line,
+ const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ RawLogVA(severity, file, line, format, ap);
+ va_end(ap);
+}
+void RawLog(int severity, const char* file, int line, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ RawLogVA(absl::NormalizeLogSeverity(severity), file, line, format, ap);
+ va_end(ap);
+}
+
+bool RawLoggingFullySupported() {
+#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
+ return true;
+#else // !ABSL_LOW_LEVEL_WRITE_SUPPORTED
+ return false;
+#endif // !ABSL_LOW_LEVEL_WRITE_SUPPORTED
+}
+
+} // namespace raw_logging_internal
+} // namespace absl
+
+namespace base_raw_logging {
+
+void RawLog(absl::LogSeverity severity, const char* file, int line,
+ const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ RawLogVA(severity, file, line, format, ap);
+ va_end(ap);
+}
+void RawLog(int severity, const char* file, int line, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ RawLogVA(absl::NormalizeLogSeverity(severity), file, line, format, ap);
+ va_end(ap);
+}
+
+void RegisterLogPrefixHook(LogPrefixHook fn) {
+ log_prefix_hook.Store(fn);
+}
+
+void RegisterAbortHook(AbortHook fn) {
+ abort_hook.Store(fn);
+}
+} // namespace base_raw_logging
+
--- /dev/null
+#include "cpp-compat.h"
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "s2/third_party/absl/base/internal/throw_delegate.h"
+
+#include <cstdlib>
+#include <functional>
+#include <new>
+#include <stdexcept>
+#include "s2/third_party/absl/base/config.h"
+#include "s2/third_party/absl/base/internal/raw_logging.h"
+
+namespace absl {
+namespace base_internal {
+
+namespace {
+template <typename T>
+[[noreturn]] void Throw(const T& error) {
+#ifdef ABSL_HAVE_EXCEPTIONS
+ throw error;
+#else
+ ABSL_RAW_LOG(FATAL, "%s", error.what());
+ cpp_compat_abort();
+#endif
+}
+} // namespace
+
+void ThrowStdLogicError(const std::string& what_arg) {
+ Throw(std::logic_error(what_arg));
+}
+void ThrowStdLogicError(const char* what_arg) {
+ Throw(std::logic_error(what_arg));
+}
+void ThrowStdInvalidArgument(const std::string& what_arg) {
+ Throw(std::invalid_argument(what_arg));
+}
+void ThrowStdInvalidArgument(const char* what_arg) {
+ Throw(std::invalid_argument(what_arg));
+}
+
+void ThrowStdDomainError(const std::string& what_arg) {
+ Throw(std::domain_error(what_arg));
+}
+void ThrowStdDomainError(const char* what_arg) {
+ Throw(std::domain_error(what_arg));
+}
+
+void ThrowStdLengthError(const std::string& what_arg) {
+ Throw(std::length_error(what_arg));
+}
+void ThrowStdLengthError(const char* what_arg) {
+ Throw(std::length_error(what_arg));
+}
+
+void ThrowStdOutOfRange(const std::string& what_arg) {
+ Throw(std::out_of_range(what_arg));
+}
+void ThrowStdOutOfRange(const char* what_arg) {
+ Throw(std::out_of_range(what_arg));
+}
+
+void ThrowStdRuntimeError(const std::string& what_arg) {
+ Throw(std::runtime_error(what_arg));
+}
+void ThrowStdRuntimeError(const char* what_arg) {
+ Throw(std::runtime_error(what_arg));
+}
+
+void ThrowStdRangeError(const std::string& what_arg) {
+ Throw(std::range_error(what_arg));
+}
+void ThrowStdRangeError(const char* what_arg) {
+ Throw(std::range_error(what_arg));
+}
+
+void ThrowStdOverflowError(const std::string& what_arg) {
+ Throw(std::overflow_error(what_arg));
+}
+void ThrowStdOverflowError(const char* what_arg) {
+ Throw(std::overflow_error(what_arg));
+}
+
+void ThrowStdUnderflowError(const std::string& what_arg) {
+ Throw(std::underflow_error(what_arg));
+}
+void ThrowStdUnderflowError(const char* what_arg) {
+ Throw(std::underflow_error(what_arg));
+}
+
+void ThrowStdBadFunctionCall() { Throw(std::bad_function_call()); }
+
+void ThrowStdBadAlloc() { Throw(std::bad_alloc()); }
+
+} // namespace base_internal
+} // namespace absl
--- /dev/null
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "s2/third_party/absl/numeric/int128.h"
+
+#include <cstddef>
+#include <cassert>
+#include <iomanip>
+#include <iostream> // NOLINT(readability/streams)
+#include <sstream>
+#include <string>
+#include <type_traits>
+
+namespace absl {
+
+const uint128 kuint128max = MakeUint128(std::numeric_limits<uint64_t>::max(),
+ std::numeric_limits<uint64_t>::max());
+
+namespace {
+
+// Returns the 0-based position of the last set bit (i.e., most significant bit)
+// in the given uint64_t. The argument may not be 0.
+//
+// For example:
+// Given: 5 (decimal) == 101 (binary)
+// Returns: 2
+#define STEP(T, n, pos, sh) \
+ do { \
+ if ((n) >= (static_cast<T>(1) << (sh))) { \
+ (n) = (n) >> (sh); \
+ (pos) |= (sh); \
+ } \
+ } while (0)
+static inline int Fls64(uint64_t n) {
+ assert(n != 0);
+ int pos = 0;
+ STEP(uint64_t, n, pos, 0x20);
+ uint32_t n32 = static_cast<uint32_t>(n);
+ STEP(uint32_t, n32, pos, 0x10);
+ STEP(uint32_t, n32, pos, 0x08);
+ STEP(uint32_t, n32, pos, 0x04);
+ return pos + ((uint64_t{0x3333333322221100} >> (n32 << 2)) & 0x3);
+}
+#undef STEP
+
+// Like Fls64() above, but returns the 0-based position of the last set bit
+// (i.e., most significant bit) in the given uint128. The argument may not be 0.
+static inline int Fls128(uint128 n) {
+ if (uint64_t hi = Uint128High64(n)) {
+ return Fls64(hi) + 64;
+ }
+ return Fls64(Uint128Low64(n));
+}
+
+// Long division/modulo for uint128 implemented using the shift-subtract
+// division algorithm adapted from:
+// http://stackoverflow.com/questions/5386377/division-without-using
+void DivModImpl(uint128 dividend, uint128 divisor, uint128* quotient_ret,
+ uint128* remainder_ret) {
+ assert(divisor != 0);
+
+ if (divisor > dividend) {
+ *quotient_ret = 0;
+ *remainder_ret = dividend;
+ return;
+ }
+
+ if (divisor == dividend) {
+ *quotient_ret = 1;
+ *remainder_ret = 0;
+ return;
+ }
+
+ uint128 denominator = divisor;
+ uint128 quotient = 0;
+
+ // Left aligns the MSB of the denominator and the dividend.
+ const int shift = Fls128(dividend) - Fls128(denominator);
+ denominator <<= shift;
+
+ // Uses shift-subtract algorithm to divide dividend by denominator. The
+ // remainder will be left in dividend.
+ for (int i = 0; i <= shift; ++i) {
+ quotient <<= 1;
+ if (dividend >= denominator) {
+ dividend -= denominator;
+ quotient |= 1;
+ }
+ denominator >>= 1;
+ }
+
+ *quotient_ret = quotient;
+ *remainder_ret = dividend;
+}
+
+template <typename T>
+uint128 MakeUint128FromFloat(T v) {
+ static_assert(std::is_floating_point<T>::value, "");
+
+ // Rounding behavior is towards zero, same as for built-in types.
+
+ // Undefined behavior if v is NaN or cannot fit into uint128.
+ assert(std::isfinite(v) && v > -1 &&
+ (std::numeric_limits<T>::max_exponent <= 128 ||
+ v < std::ldexp(static_cast<T>(1), 128)));
+
+ if (v >= std::ldexp(static_cast<T>(1), 64)) {
+ uint64_t hi = static_cast<uint64_t>(std::ldexp(v, -64));
+ uint64_t lo = static_cast<uint64_t>(v - std::ldexp(static_cast<T>(hi), 64));
+ return MakeUint128(hi, lo);
+ }
+
+ return MakeUint128(0, static_cast<uint64_t>(v));
+}
+} // namespace
+
+uint128::uint128(float v) : uint128(MakeUint128FromFloat(v)) {}
+uint128::uint128(double v) : uint128(MakeUint128FromFloat(v)) {}
+uint128::uint128(long double v) : uint128(MakeUint128FromFloat(v)) {}
+
+uint128 operator/(uint128 lhs, uint128 rhs) {
+#if defined(ABSL_HAVE_INTRINSIC_INT128)
+ return __extension__ static_cast<unsigned __int128>(lhs) /
+ __extension__ static_cast<unsigned __int128>(rhs);
+#else // ABSL_HAVE_INTRINSIC_INT128
+ uint128 quotient = 0;
+ uint128 remainder = 0;
+ DivModImpl(lhs, rhs, "ient, &remainder);
+ return quotient;
+#endif // ABSL_HAVE_INTRINSIC_INT128
+}
+uint128 operator%(uint128 lhs, uint128 rhs) {
+#if defined(ABSL_HAVE_INTRINSIC_INT128)
+ return __extension__ static_cast<unsigned __int128>(lhs) %
+ __extension__ static_cast<unsigned __int128>(rhs);
+#else // ABSL_HAVE_INTRINSIC_INT128
+ uint128 quotient = 0;
+ uint128 remainder = 0;
+ DivModImpl(lhs, rhs, "ient, &remainder);
+ return remainder;
+#endif // ABSL_HAVE_INTRINSIC_INT128
+}
+
+namespace {
+
+std::string Uint128ToFormattedString(uint128 v, std::ios_base::fmtflags flags) {
+ // Select a divisor which is the largest power of the base < 2^64.
+ uint128 div;
+ int div_base_log;
+ switch (flags & std::ios::basefield) {
+ case std::ios::hex:
+ div = 0x1000000000000000; // 16^15
+ div_base_log = 15;
+ break;
+ case std::ios::oct:
+ div = 01000000000000000000000; // 8^21
+ div_base_log = 21;
+ break;
+ default: // std::ios::dec
+ div = 10000000000000000000u; // 10^19
+ div_base_log = 19;
+ break;
+ }
+
+ // Now piece together the uint128 representation from three chunks of the
+ // original value, each less than "div" and therefore representable as a
+ // uint64_t.
+ std::ostringstream os;
+ std::ios_base::fmtflags copy_mask =
+ std::ios::basefield | std::ios::showbase | std::ios::uppercase;
+ os.setf(flags & copy_mask, copy_mask);
+ uint128 high = v;
+ uint128 low;
+ DivModImpl(high, div, &high, &low);
+ uint128 mid;
+ DivModImpl(high, div, &high, &mid);
+ if (Uint128Low64(high) != 0) {
+ os << Uint128Low64(high);
+ os << std::noshowbase << std::setfill('0') << std::setw(div_base_log);
+ os << Uint128Low64(mid);
+ os << std::setw(div_base_log);
+ } else if (Uint128Low64(mid) != 0) {
+ os << Uint128Low64(mid);
+ os << std::noshowbase << std::setfill('0') << std::setw(div_base_log);
+ }
+ os << Uint128Low64(low);
+ return os.str();
+}
+
+} // namespace
+
+std::ostream& operator<<(std::ostream& os, uint128 v) {
+ std::ios_base::fmtflags flags = os.flags();
+ std::string rep = Uint128ToFormattedString(v, flags);
+
+ // Add the requisite padding.
+ std::streamsize width = os.width(0);
+ if (static_cast<size_t>(width) > rep.size()) {
+ std::ios::fmtflags adjustfield = flags & std::ios::adjustfield;
+ if (adjustfield == std::ios::left) {
+ rep.append(width - rep.size(), os.fill());
+ } else if (adjustfield == std::ios::internal &&
+ (flags & std::ios::showbase) &&
+ (flags & std::ios::basefield) == std::ios::hex && v != 0) {
+ rep.insert(2, width - rep.size(), os.fill());
+ } else {
+ rep.insert(0, width - rep.size(), os.fill());
+ }
+ }
+
+ return os << rep;
+}
+
+namespace {
+
+}
+
+} // namespace absl
+
+
+
--- /dev/null
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "s2/third_party/absl/strings/ascii.h"
+
+namespace absl {
+namespace ascii_internal {
+
+// # Table generated by this Python code (bit 0x02 is currently unused):
+// TODO(user) Move Python code for generation of table to BUILD and link here.
+
+// NOTE: The kAsciiPropertyBits table used within this code was generated by
+// Python code of the following form. (Bit 0x02 is currently unused and
+// available.)
+//
+// def Hex2(n):
+// return '0x' + hex(n/16)[2:] + hex(n%16)[2:]
+// def IsPunct(ch):
+// return (ord(ch) >= 32 and ord(ch) < 127 and
+// not ch.isspace() and not ch.isalnum())
+// def IsBlank(ch):
+// return ch in ' \t'
+// def IsCntrl(ch):
+// return ord(ch) < 32 or ord(ch) == 127
+// def IsXDigit(ch):
+// return ch.isdigit() or ch.lower() in 'abcdef'
+// for i in range(128):
+// ch = chr(i)
+// mask = ((ch.isalpha() and 0x01 or 0) |
+// (ch.isalnum() and 0x04 or 0) |
+// (ch.isspace() and 0x08 or 0) |
+// (IsPunct(ch) and 0x10 or 0) |
+// (IsBlank(ch) and 0x20 or 0) |
+// (IsCntrl(ch) and 0x40 or 0) |
+// (IsXDigit(ch) and 0x80 or 0))
+// print Hex2(mask) + ',',
+// if i % 16 == 7:
+// print ' //', Hex2(i & 0x78)
+// elif i % 16 == 15:
+// print
+
+// clang-format off
+// Array of bitfields holding character information. Each bit value corresponds
+// to a particular character feature. For readability, and because the value
+// of these bits is tightly coupled to this implementation, the individual bits
+// are not named. Note that bitfields for all characters above ASCII 127 are
+// zero-initialized.
+const unsigned char kPropertyBits[256] = {
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, // 0x00
+ 0x40, 0x68, 0x48, 0x48, 0x48, 0x48, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, // 0x10
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x28, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, // 0x20
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, // 0x30
+ 0x84, 0x84, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x05, // 0x40
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x50
+ 0x05, 0x05, 0x05, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x05, // 0x60
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x70
+ 0x05, 0x05, 0x05, 0x10, 0x10, 0x10, 0x10, 0x40,
+};
+
+// Array of characters for the ascii_tolower() function. For values 'A'
+// through 'Z', return the lower-case character; otherwise, return the
+// identity of the passed character.
+const char kToLower[256] = {
+ '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
+ '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
+ '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
+ '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
+ '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
+ '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
+ '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
+ '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
+ '\x40', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+ 'x', 'y', 'z', '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
+ '\x60', '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67',
+ '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f',
+ '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77',
+ '\x78', '\x79', '\x7a', '\x7b', '\x7c', '\x7d', '\x7e', '\x7f',
+ '\x80', '\x81', '\x82', '\x83', '\x84', '\x85', '\x86', '\x87',
+ '\x88', '\x89', '\x8a', '\x8b', '\x8c', '\x8d', '\x8e', '\x8f',
+ '\x90', '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97',
+ '\x98', '\x99', '\x9a', '\x9b', '\x9c', '\x9d', '\x9e', '\x9f',
+ '\xa0', '\xa1', '\xa2', '\xa3', '\xa4', '\xa5', '\xa6', '\xa7',
+ '\xa8', '\xa9', '\xaa', '\xab', '\xac', '\xad', '\xae', '\xaf',
+ '\xb0', '\xb1', '\xb2', '\xb3', '\xb4', '\xb5', '\xb6', '\xb7',
+ '\xb8', '\xb9', '\xba', '\xbb', '\xbc', '\xbd', '\xbe', '\xbf',
+ '\xc0', '\xc1', '\xc2', '\xc3', '\xc4', '\xc5', '\xc6', '\xc7',
+ '\xc8', '\xc9', '\xca', '\xcb', '\xcc', '\xcd', '\xce', '\xcf',
+ '\xd0', '\xd1', '\xd2', '\xd3', '\xd4', '\xd5', '\xd6', '\xd7',
+ '\xd8', '\xd9', '\xda', '\xdb', '\xdc', '\xdd', '\xde', '\xdf',
+ '\xe0', '\xe1', '\xe2', '\xe3', '\xe4', '\xe5', '\xe6', '\xe7',
+ '\xe8', '\xe9', '\xea', '\xeb', '\xec', '\xed', '\xee', '\xef',
+ '\xf0', '\xf1', '\xf2', '\xf3', '\xf4', '\xf5', '\xf6', '\xf7',
+ '\xf8', '\xf9', '\xfa', '\xfb', '\xfc', '\xfd', '\xfe', '\xff',
+};
+
+// Array of characters for the ascii_toupper() function. For values 'a'
+// through 'z', return the upper-case character; otherwise, return the
+// identity of the passed character.
+const char kToUpper[256] = {
+ '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
+ '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
+ '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
+ '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
+ '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
+ '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
+ '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
+ '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
+ '\x40', '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47',
+ '\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f',
+ '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57',
+ '\x58', '\x59', '\x5a', '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
+ '\x60', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
+ 'X', 'Y', 'Z', '\x7b', '\x7c', '\x7d', '\x7e', '\x7f',
+ '\x80', '\x81', '\x82', '\x83', '\x84', '\x85', '\x86', '\x87',
+ '\x88', '\x89', '\x8a', '\x8b', '\x8c', '\x8d', '\x8e', '\x8f',
+ '\x90', '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97',
+ '\x98', '\x99', '\x9a', '\x9b', '\x9c', '\x9d', '\x9e', '\x9f',
+ '\xa0', '\xa1', '\xa2', '\xa3', '\xa4', '\xa5', '\xa6', '\xa7',
+ '\xa8', '\xa9', '\xaa', '\xab', '\xac', '\xad', '\xae', '\xaf',
+ '\xb0', '\xb1', '\xb2', '\xb3', '\xb4', '\xb5', '\xb6', '\xb7',
+ '\xb8', '\xb9', '\xba', '\xbb', '\xbc', '\xbd', '\xbe', '\xbf',
+ '\xc0', '\xc1', '\xc2', '\xc3', '\xc4', '\xc5', '\xc6', '\xc7',
+ '\xc8', '\xc9', '\xca', '\xcb', '\xcc', '\xcd', '\xce', '\xcf',
+ '\xd0', '\xd1', '\xd2', '\xd3', '\xd4', '\xd5', '\xd6', '\xd7',
+ '\xd8', '\xd9', '\xda', '\xdb', '\xdc', '\xdd', '\xde', '\xdf',
+ '\xe0', '\xe1', '\xe2', '\xe3', '\xe4', '\xe5', '\xe6', '\xe7',
+ '\xe8', '\xe9', '\xea', '\xeb', '\xec', '\xed', '\xee', '\xef',
+ '\xf0', '\xf1', '\xf2', '\xf3', '\xf4', '\xf5', '\xf6', '\xf7',
+ '\xf8', '\xf9', '\xfa', '\xfb', '\xfc', '\xfd', '\xfe', '\xff',
+};
+// clang-format on
+
+} // namespace ascii_internal
+
+void AsciiStrToLower(string* s) {
+ for (auto& ch : *s) {
+ ch = absl::ascii_tolower(ch);
+ }
+}
+
+void AsciiStrToUpper(string* s) {
+ for (auto& ch : *s) {
+ ch = absl::ascii_toupper(ch);
+ }
+}
+
+void RemoveExtraAsciiWhitespace(string* str) {
+ auto stripped = StripAsciiWhitespace(*str);
+
+ if (stripped.empty()) {
+ str->clear();
+ return;
+ }
+
+ auto input_it = stripped.begin();
+ auto input_end = stripped.end();
+ auto output_it = &(*str)[0];
+ bool is_ws = false;
+
+ for (; input_it < input_end; ++input_it) {
+ if (is_ws) {
+ // Consecutive whitespace? Keep only the last.
+ is_ws = absl::ascii_isspace(*input_it);
+ if (is_ws) --output_it;
+ } else {
+ is_ws = absl::ascii_isspace(*input_it);
+ }
+
+ *output_it = *input_it;
+ ++output_it;
+ }
+
+ str->erase(output_it - &(*str)[0]);
+}
+
+} // namespace absl
--- /dev/null
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "s2/third_party/absl/strings/internal/memutil.h"
+
+#include <cstdlib>
+
+namespace absl {
+namespace strings_internal {
+
+int memcasecmp(const char* s1, const char* s2, size_t len) {
+ const unsigned char* us1 = reinterpret_cast<const unsigned char*>(s1);
+ const unsigned char* us2 = reinterpret_cast<const unsigned char*>(s2);
+
+ for (size_t i = 0; i < len; i++) {
+ const int diff =
+ int{static_cast<unsigned char>(absl::ascii_tolower(us1[i]))} -
+ int{static_cast<unsigned char>(absl::ascii_tolower(us2[i]))};
+ if (diff != 0) return diff;
+ }
+ return 0;
+}
+
+char* memdup(const char* s, size_t slen) {
+ void* copy;
+ if ((copy = malloc(slen)) == nullptr) return nullptr;
+ memcpy(copy, s, slen);
+ return reinterpret_cast<char*>(copy);
+}
+
+char* memrchr(const char* s, int c, size_t slen) {
+ for (const char* e = s + slen - 1; e >= s; e--) {
+ if (*e == c) return const_cast<char*>(e);
+ }
+ return nullptr;
+}
+
+size_t memspn(const char* s, size_t slen, const char* accept) {
+ const char* p = s;
+ const char* spanp;
+ char c, sc;
+
+cont:
+ c = *p++;
+ if (slen-- == 0) return p - 1 - s;
+ for (spanp = accept; (sc = *spanp++) != '\0';)
+ if (sc == c) goto cont;
+ return p - 1 - s;
+}
+
+size_t memcspn(const char* s, size_t slen, const char* reject) {
+ const char* p = s;
+ const char* spanp;
+ char c, sc;
+
+ while (slen-- != 0) {
+ c = *p++;
+ for (spanp = reject; (sc = *spanp++) != '\0';)
+ if (sc == c) return p - 1 - s;
+ }
+ return p - s;
+}
+
+char* mempbrk(const char* s, size_t slen, const char* accept) {
+ const char* scanp;
+ int sc;
+
+ for (; slen; ++s, --slen) {
+ for (scanp = accept; (sc = *scanp++) != '\0';)
+ if (sc == *s) return const_cast<char*>(s);
+ }
+ return nullptr;
+}
+
+// This is significantly faster for case-sensitive matches with very
+// few possible matches. See unit test for benchmarks.
+const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle,
+ size_t neelen) {
+ if (0 == neelen) {
+ return phaystack; // even if haylen is 0
+ }
+ if (haylen < neelen) return nullptr;
+
+ const char* match;
+ const char* hayend = phaystack + haylen - neelen + 1;
+ // A static cast is used here to work around the fact that memchr returns
+ // a void* on Posix-compliant systems and const void* on Windows.
+ while ((match = static_cast<const char*>(
+ memchr(phaystack, pneedle[0], hayend - phaystack)))) {
+ if (memcmp(match, pneedle, neelen) == 0)
+ return match;
+ else
+ phaystack = match + 1;
+ }
+ return nullptr;
+}
+
+} // namespace strings_internal
+} // namespace absl
--- /dev/null
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "s2/third_party/absl/strings/match.h"
+
+#include "s2/third_party/absl/strings/internal/memutil.h"
+
+namespace absl {
+
+bool EqualsIgnoreCase(absl::string_view piece1, absl::string_view piece2) {
+ return (piece1.size() == piece2.size() &&
+ 0 == absl::strings_internal::memcasecmp(piece1.data(), piece2.data(),
+ piece1.size()));
+ // memcasecmp uses absl::ascii_tolower().
+}
+
+bool StartsWithIgnoreCase(absl::string_view text, absl::string_view prefix) {
+ return (text.size() >= prefix.size()) &&
+ EqualsIgnoreCase(text.substr(0, prefix.size()), prefix);
+}
+
+bool EndsWithIgnoreCase(absl::string_view text, absl::string_view suffix) {
+ return (text.size() >= suffix.size()) &&
+ EqualsIgnoreCase(text.substr(text.size() - suffix.size()), suffix);
+}
+
+} // namespace absl
--- /dev/null
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This file contains string processing functions related to
+// numeric values.
+
+#include "s2/third_party/absl/strings/numbers.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cfloat> // for DBL_DIG and FLT_DIG
+#include <cmath> // for HUGE_VAL
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <utility>
+
+#include "s2/third_party/absl/base/internal/raw_logging.h"
+#include "s2/third_party/absl/strings/ascii.h"
+#include "s2/third_party/absl/strings/internal/bits.h"
+#include "s2/third_party/absl/strings/internal/memutil.h"
+#include "s2/third_party/absl/strings/str_cat.h"
+
+namespace absl {
+
+bool SimpleAtof(absl::string_view str, float* value) {
+ *value = 0.0;
+ if (str.empty()) return false;
+ char buf[32];
+ std::unique_ptr<char[]> bigbuf;
+ char* ptr = buf;
+ if (str.size() > sizeof(buf) - 1) {
+ bigbuf.reset(new char[str.size() + 1]);
+ ptr = bigbuf.get();
+ }
+ memcpy(ptr, str.data(), str.size());
+ ptr[str.size()] = '\0';
+
+ char* endptr;
+ *value = strtof(ptr, &endptr);
+ if (endptr != ptr) {
+ while (absl::ascii_isspace(*endptr)) ++endptr;
+ }
+ // Ignore range errors from strtod/strtof.
+ // The values it returns on underflow and
+ // overflow are the right fallback in a
+ // robust setting.
+ return *ptr != '\0' && *endptr == '\0';
+}
+
+bool SimpleAtod(absl::string_view str, double* value) {
+ *value = 0.0;
+ if (str.empty()) return false;
+ char buf[32];
+ std::unique_ptr<char[]> bigbuf;
+ char* ptr = buf;
+ if (str.size() > sizeof(buf) - 1) {
+ bigbuf.reset(new char[str.size() + 1]);
+ ptr = bigbuf.get();
+ }
+ memcpy(ptr, str.data(), str.size());
+ ptr[str.size()] = '\0';
+
+ char* endptr;
+ *value = strtod(ptr, &endptr);
+ if (endptr != ptr) {
+ while (absl::ascii_isspace(*endptr)) ++endptr;
+ }
+ // Ignore range errors from strtod. The values it
+ // returns on underflow and overflow are the right
+ // fallback in a robust setting.
+ return *ptr != '\0' && *endptr == '\0';
+}
+
+namespace {
+
+// TODO(user): replace with the real released thing once we figure out what
+// it is.
+inline bool CaseEqual(absl::string_view piece1, absl::string_view piece2) {
+ return (piece1.size() == piece2.size() &&
+ 0 == strings_internal::memcasecmp(piece1.data(), piece2.data(),
+ piece1.size()));
+}
+
+// Writes a two-character representation of 'i' to 'buf'. 'i' must be in the
+// range 0 <= i < 100, and buf must have space for two characters. Example:
+// char buf[2];
+// PutTwoDigits(42, buf);
+// // buf[0] == '4'
+// // buf[1] == '2'
+inline void PutTwoDigits(size_t i, char* buf) {
+ static const char two_ASCII_digits[100][2] = {
+ {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'},
+ {'0', '5'}, {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'},
+ {'1', '0'}, {'1', '1'}, {'1', '2'}, {'1', '3'}, {'1', '4'},
+ {'1', '5'}, {'1', '6'}, {'1', '7'}, {'1', '8'}, {'1', '9'},
+ {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, {'2', '4'},
+ {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'},
+ {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'},
+ {'3', '5'}, {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'},
+ {'4', '0'}, {'4', '1'}, {'4', '2'}, {'4', '3'}, {'4', '4'},
+ {'4', '5'}, {'4', '6'}, {'4', '7'}, {'4', '8'}, {'4', '9'},
+ {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, {'5', '4'},
+ {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'},
+ {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'},
+ {'6', '5'}, {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'},
+ {'7', '0'}, {'7', '1'}, {'7', '2'}, {'7', '3'}, {'7', '4'},
+ {'7', '5'}, {'7', '6'}, {'7', '7'}, {'7', '8'}, {'7', '9'},
+ {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, {'8', '4'},
+ {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'},
+ {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'},
+ {'9', '5'}, {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}
+ };
+ assert(i < 100);
+ memcpy(buf, two_ASCII_digits[i], 2);
+}
+
+} // namespace
+
+bool SimpleAtob(absl::string_view str, bool* value) {
+ ABSL_RAW_CHECK(value != nullptr, "Output pointer must not be nullptr.");
+ if (CaseEqual(str, "true") || CaseEqual(str, "t") ||
+ CaseEqual(str, "yes") || CaseEqual(str, "y") ||
+ CaseEqual(str, "1")) {
+ *value = true;
+ return true;
+ }
+ if (CaseEqual(str, "false") || CaseEqual(str, "f") ||
+ CaseEqual(str, "no") || CaseEqual(str, "n") ||
+ CaseEqual(str, "0")) {
+ *value = false;
+ return true;
+ }
+ return false;
+}
+
+// ----------------------------------------------------------------------
+// FastIntToBuffer() overloads
+//
+// Like the Fast*ToBuffer() functions above, these are intended for speed.
+// Unlike the Fast*ToBuffer() functions, however, these functions write
+// their output to the beginning of the buffer. The caller is responsible
+// for ensuring that the buffer has enough space to hold the output.
+//
+// Returns a pointer to the end of the string (i.e. the null character
+// terminating the string).
+// ----------------------------------------------------------------------
+
+namespace {
+
+// Used to optimize printing a decimal number's final digit.
+const char one_ASCII_final_digits[10][2] {
+ {'0', 0}, {'1', 0}, {'2', 0}, {'3', 0}, {'4', 0},
+ {'5', 0}, {'6', 0}, {'7', 0}, {'8', 0}, {'9', 0},
+};
+
+} // namespace
+
+char* numbers_internal::FastIntToBuffer(uint32_t i, char* buffer) {
+ uint32_t digits;
+ // The idea of this implementation is to trim the number of divides to as few
+ // as possible, and also reducing memory stores and branches, by going in
+ // steps of two digits at a time rather than one whenever possible.
+ // The huge-number case is first, in the hopes that the compiler will output
+ // that case in one branch-free block of code, and only output conditional
+ // branches into it from below.
+ if (i >= 1000000000) { // >= 1,000,000,000
+ digits = i / 100000000; // 100,000,000
+ i -= digits * 100000000;
+ PutTwoDigits(digits, buffer);
+ buffer += 2;
+ lt100_000_000:
+ digits = i / 1000000; // 1,000,000
+ i -= digits * 1000000;
+ PutTwoDigits(digits, buffer);
+ buffer += 2;
+ lt1_000_000:
+ digits = i / 10000; // 10,000
+ i -= digits * 10000;
+ PutTwoDigits(digits, buffer);
+ buffer += 2;
+ lt10_000:
+ digits = i / 100;
+ i -= digits * 100;
+ PutTwoDigits(digits, buffer);
+ buffer += 2;
+ lt100:
+ digits = i;
+ PutTwoDigits(digits, buffer);
+ buffer += 2;
+ *buffer = 0;
+ return buffer;
+ }
+
+ if (i < 100) {
+ digits = i;
+ if (i >= 10) goto lt100;
+ memcpy(buffer, one_ASCII_final_digits[i], 2);
+ return buffer + 1;
+ }
+ if (i < 10000) { // 10,000
+ if (i >= 1000) goto lt10_000;
+ digits = i / 100;
+ i -= digits * 100;
+ *buffer++ = '0' + digits;
+ goto lt100;
+ }
+ if (i < 1000000) { // 1,000,000
+ if (i >= 100000) goto lt1_000_000;
+ digits = i / 10000; // 10,000
+ i -= digits * 10000;
+ *buffer++ = '0' + digits;
+ goto lt10_000;
+ }
+ if (i < 100000000) { // 100,000,000
+ if (i >= 10000000) goto lt100_000_000;
+ digits = i / 1000000; // 1,000,000
+ i -= digits * 1000000;
+ *buffer++ = '0' + digits;
+ goto lt1_000_000;
+ }
+ // we already know that i < 1,000,000,000
+ digits = i / 100000000; // 100,000,000
+ i -= digits * 100000000;
+ *buffer++ = '0' + digits;
+ goto lt100_000_000;
+}
+
+char* numbers_internal::FastIntToBuffer(int32_t i, char* buffer) {
+ uint32_t u = i;
+ if (i < 0) {
+ *buffer++ = '-';
+ // We need to do the negation in modular (i.e., "unsigned")
+ // arithmetic; MSVC++ apprently warns for plain "-u", so
+ // we write the equivalent expression "0 - u" instead.
+ u = 0 - u;
+ }
+ return numbers_internal::FastIntToBuffer(u, buffer);
+}
+
+char* numbers_internal::FastIntToBuffer(uint64_t i, char* buffer) {
+ uint32_t u32 = static_cast<uint32_t>(i);
+ if (u32 == i) return numbers_internal::FastIntToBuffer(u32, buffer);
+
+ // Here we know i has at least 10 decimal digits.
+ uint64_t top_1to11 = i / 1000000000;
+ u32 = static_cast<uint32_t>(i - top_1to11 * 1000000000);
+ uint32_t top_1to11_32 = static_cast<uint32_t>(top_1to11);
+
+ if (top_1to11_32 == top_1to11) {
+ buffer = numbers_internal::FastIntToBuffer(top_1to11_32, buffer);
+ } else {
+ // top_1to11 has more than 32 bits too; print it in two steps.
+ uint32_t top_8to9 = static_cast<uint32_t>(top_1to11 / 100);
+ uint32_t mid_2 = static_cast<uint32_t>(top_1to11 - top_8to9 * 100);
+ buffer = numbers_internal::FastIntToBuffer(top_8to9, buffer);
+ PutTwoDigits(mid_2, buffer);
+ buffer += 2;
+ }
+
+ // We have only 9 digits now, again the maximum uint32_t can handle fully.
+ uint32_t digits = u32 / 10000000; // 10,000,000
+ u32 -= digits * 10000000;
+ PutTwoDigits(digits, buffer);
+ buffer += 2;
+ digits = u32 / 100000; // 100,000
+ u32 -= digits * 100000;
+ PutTwoDigits(digits, buffer);
+ buffer += 2;
+ digits = u32 / 1000; // 1,000
+ u32 -= digits * 1000;
+ PutTwoDigits(digits, buffer);
+ buffer += 2;
+ digits = u32 / 10;
+ u32 -= digits * 10;
+ PutTwoDigits(digits, buffer);
+ buffer += 2;
+ memcpy(buffer, one_ASCII_final_digits[u32], 2);
+ return buffer + 1;
+}
+
+char* numbers_internal::FastIntToBuffer(int64_t i, char* buffer) {
+ uint64_t u = i;
+ if (i < 0) {
+ *buffer++ = '-';
+ u = 0 - u;
+ }
+ return numbers_internal::FastIntToBuffer(u, buffer);
+}
+
+
+// Given a 128-bit number expressed as a pair of uint64_t, high half first,
+// return that number multiplied by the given 32-bit value. If the result is
+// too large to fit in a 128-bit number, divide it by 2 until it fits.
+static std::pair<uint64_t, uint64_t> Mul32(std::pair<uint64_t, uint64_t> num,
+ uint32_t mul) {
+ uint64_t bits0_31 = num.second & 0xFFFFFFFF;
+ uint64_t bits32_63 = num.second >> 32;
+ uint64_t bits64_95 = num.first & 0xFFFFFFFF;
+ uint64_t bits96_127 = num.first >> 32;
+
+ // The picture so far: each of these 64-bit values has only the lower 32 bits
+ // filled in.
+ // bits96_127: [ 00000000 xxxxxxxx ]
+ // bits64_95: [ 00000000 xxxxxxxx ]
+ // bits32_63: [ 00000000 xxxxxxxx ]
+ // bits0_31: [ 00000000 xxxxxxxx ]
+
+ bits0_31 *= mul;
+ bits32_63 *= mul;
+ bits64_95 *= mul;
+ bits96_127 *= mul;
+
+ // Now the top halves may also have value, though all 64 of their bits will
+ // never be set at the same time, since they are a result of a 32x32 bit
+ // multiply. This makes the carry calculation slightly easier.
+ // bits96_127: [ mmmmmmmm | mmmmmmmm ]
+ // bits64_95: [ | mmmmmmmm mmmmmmmm | ]
+ // bits32_63: | [ mmmmmmmm | mmmmmmmm ]
+ // bits0_31: | [ | mmmmmmmm mmmmmmmm ]
+ // eventually: [ bits128_up | ...bits64_127.... | ..bits0_63... ]
+
+ uint64_t bits0_63 = bits0_31 + (bits32_63 << 32);
+ uint64_t bits64_127 = bits64_95 + (bits96_127 << 32) + (bits32_63 >> 32) +
+ (bits0_63 < bits0_31);
+ uint64_t bits128_up = (bits96_127 >> 32) + (bits64_127 < bits64_95);
+ if (bits128_up == 0) return {bits64_127, bits0_63};
+
+ int shift = 64 - strings_internal::CountLeadingZeros64(bits128_up);
+ uint64_t lo = (bits0_63 >> shift) + (bits64_127 << (64 - shift));
+ uint64_t hi = (bits64_127 >> shift) + (bits128_up << (64 - shift));
+ return {hi, lo};
+}
+
+// Compute num * 5 ^ expfive, and return the first 128 bits of the result,
+// where the first bit is always a one. So PowFive(1, 0) starts 0b100000,
+// PowFive(1, 1) starts 0b101000, PowFive(1, 2) starts 0b110010, etc.
+static std::pair<uint64_t, uint64_t> PowFive(uint64_t num, int expfive) {
+ std::pair<uint64_t, uint64_t> result = {num, 0};
+ while (expfive >= 13) {
+ // 5^13 is the highest power of five that will fit in a 32-bit integer.
+ result = Mul32(result, 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5);
+ expfive -= 13;
+ }
+ constexpr int powers_of_five[13] = {
+ 1,
+ 5,
+ 5 * 5,
+ 5 * 5 * 5,
+ 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5};
+ result = Mul32(result, powers_of_five[expfive & 15]);
+ int shift = strings_internal::CountLeadingZeros64(result.first);
+ if (shift != 0) {
+ result.first = (result.first << shift) + (result.second >> (64 - shift));
+ result.second = (result.second << shift);
+ }
+ return result;
+}
+
+struct ExpDigits {
+ int32_t exponent;
+ char digits[6];
+};
+
+// SplitToSix converts value, a positive double-precision floating-point number,
+// into a base-10 exponent and 6 ASCII digits, where the first digit is never
+// zero. For example, SplitToSix(1) returns an exponent of zero and a digits
+// array of {'1', '0', '0', '0', '0', '0'}. If value is exactly halfway between
+// two possible representations, e.g. value = 100000.5, then "round to even" is
+// performed.
+static ExpDigits SplitToSix(const double value) {
+ ExpDigits exp_dig;
+ int exp = 5;
+ double d = value;
+ // First step: calculate a close approximation of the output, where the
+ // value d will be between 100,000 and 999,999, representing the digits
+ // in the output ASCII array, and exp is the base-10 exponent. It would be
+ // faster to use a table here, and to look up the base-2 exponent of value,
+ // however value is an IEEE-754 64-bit number, so the table would have 2,000
+ // entries, which is not cache-friendly.
+ if (d >= 999999.5) {
+ if (d >= 1e+261) exp += 256, d *= 1e-256;
+ if (d >= 1e+133) exp += 128, d *= 1e-128;
+ if (d >= 1e+69) exp += 64, d *= 1e-64;
+ if (d >= 1e+37) exp += 32, d *= 1e-32;
+ if (d >= 1e+21) exp += 16, d *= 1e-16;
+ if (d >= 1e+13) exp += 8, d *= 1e-8;
+ if (d >= 1e+9) exp += 4, d *= 1e-4;
+ if (d >= 1e+7) exp += 2, d *= 1e-2;
+ if (d >= 1e+6) exp += 1, d *= 1e-1;
+ } else {
+ if (d < 1e-250) exp -= 256, d *= 1e256;
+ if (d < 1e-122) exp -= 128, d *= 1e128;
+ if (d < 1e-58) exp -= 64, d *= 1e64;
+ if (d < 1e-26) exp -= 32, d *= 1e32;
+ if (d < 1e-10) exp -= 16, d *= 1e16;
+ if (d < 1e-2) exp -= 8, d *= 1e8;
+ if (d < 1e+2) exp -= 4, d *= 1e4;
+ if (d < 1e+4) exp -= 2, d *= 1e2;
+ if (d < 1e+5) exp -= 1, d *= 1e1;
+ }
+ // At this point, d is in the range [99999.5..999999.5) and exp is in the
+ // range [-324..308]. Since we need to round d up, we want to add a half
+ // and truncate.
+ // However, the technique above may have lost some precision, due to its
+ // repeated multiplication by constants that each may be off by half a bit
+ // of precision. This only matters if we're close to the edge though.
+ // Since we'd like to know if the fractional part of d is close to a half,
+ // we multiply it by 65536 and see if the fractional part is close to 32768.
+ // (The number doesn't have to be a power of two,but powers of two are faster)
+ uint64_t d64k = d * 65536;
+ int dddddd; // A 6-digit decimal integer.
+ if ((d64k % 65536) == 32767 || (d64k % 65536) == 32768) {
+ // OK, it's fairly likely that precision was lost above, which is
+ // not a surprise given only 52 mantissa bits are available. Therefore
+ // redo the calculation using 128-bit numbers. (64 bits are not enough).
+
+ // Start out with digits rounded down; maybe add one below.
+ dddddd = static_cast<int>(d64k / 65536);
+
+ // mantissa is a 64-bit integer representing M.mmm... * 2^63. The actual
+ // value we're representing, of course, is M.mmm... * 2^exp2.
+ int exp2;
+ double m = std::frexp(value, &exp2);
+ uint64_t mantissa = m * (32768.0 * 65536.0 * 65536.0 * 65536.0);
+ // std::frexp returns an m value in the range [0.5, 1.0), however we
+ // can't multiply it by 2^64 and convert to an integer because some FPUs
+ // throw an exception when converting an number higher than 2^63 into an
+ // integer - even an unsigned 64-bit integer! Fortunately it doesn't matter
+ // since m only has 52 significant bits anyway.
+ mantissa <<= 1;
+ exp2 -= 64; // not needed, but nice for debugging
+
+ // OK, we are here to compare:
+ // (dddddd + 0.5) * 10^(exp-5) vs. mantissa * 2^exp2
+ // so we can round up dddddd if appropriate. Those values span the full
+ // range of 600 orders of magnitude of IEE 64-bit floating-point.
+ // Fortunately, we already know they are very close, so we don't need to
+ // track the base-2 exponent of both sides. This greatly simplifies the
+ // the math since the 2^exp2 calculation is unnecessary and the power-of-10
+ // calculation can become a power-of-5 instead.
+
+ std::pair<uint64_t, uint64_t> edge, val;
+ if (exp >= 6) {
+ // Compare (dddddd + 0.5) * 5 ^ (exp - 5) to mantissa
+ // Since we're tossing powers of two, 2 * dddddd + 1 is the
+ // same as dddddd + 0.5
+ edge = PowFive(2 * dddddd + 1, exp - 5);
+
+ val.first = mantissa;
+ val.second = 0;
+ } else {
+ // We can't compare (dddddd + 0.5) * 5 ^ (exp - 5) to mantissa as we did
+ // above because (exp - 5) is negative. So we compare (dddddd + 0.5) to
+ // mantissa * 5 ^ (5 - exp)
+ edge = PowFive(2 * dddddd + 1, 0);
+
+ val = PowFive(mantissa, 5 - exp);
+ }
+ // printf("exp=%d %016lx %016lx vs %016lx %016lx\n", exp, val.first,
+ // val.second, edge.first, edge.second);
+ if (val > edge) {
+ dddddd++;
+ } else if (val == edge) {
+ dddddd += (dddddd & 1);
+ }
+ } else {
+ // Here, we are not close to the edge.
+ dddddd = static_cast<int>((d64k + 32768) / 65536);
+ }
+ if (dddddd == 1000000) {
+ dddddd = 100000;
+ exp += 1;
+ }
+ exp_dig.exponent = exp;
+
+ int two_digits = dddddd / 10000;
+ dddddd -= two_digits * 10000;
+ PutTwoDigits(two_digits, &exp_dig.digits[0]);
+
+ two_digits = dddddd / 100;
+ dddddd -= two_digits * 100;
+ PutTwoDigits(two_digits, &exp_dig.digits[2]);
+
+ PutTwoDigits(dddddd, &exp_dig.digits[4]);
+ return exp_dig;
+}
+
+// Helper function for fast formatting of floating-point.
+// The result is the same as "%g", a.k.a. "%.6g".
+size_t numbers_internal::SixDigitsToBuffer(double d, char* const buffer) {
+ static_assert(std::numeric_limits<float>::is_iec559,
+ "IEEE-754/IEC-559 support only");
+
+ char* out = buffer; // we write data to out, incrementing as we go, but
+ // FloatToBuffer always returns the address of the buffer
+ // passed in.
+
+ if (std::isnan(d)) {
+ strcpy(out, "nan"); // NOLINT(runtime/printf)
+ return 3;
+ }
+ if (d == 0) { // +0 and -0 are handled here
+ if (std::signbit(d)) *out++ = '-';
+ *out++ = '0';
+ *out = 0;
+ return out - buffer;
+ }
+ if (d < 0) {
+ *out++ = '-';
+ d = -d;
+ }
+ if (std::isinf(d)) {
+ strcpy(out, "inf"); // NOLINT(runtime/printf)
+ return out + 3 - buffer;
+ }
+
+ auto exp_dig = SplitToSix(d);
+ int exp = exp_dig.exponent;
+ const char* digits = exp_dig.digits;
+ out[0] = '0';
+ out[1] = '.';
+ switch (exp) {
+ case 5:
+ memcpy(out, &digits[0], 6), out += 6;
+ *out = 0;
+ return out - buffer;
+ case 4:
+ memcpy(out, &digits[0], 5), out += 5;
+ if (digits[5] != '0') {
+ *out++ = '.';
+ *out++ = digits[5];
+ }
+ *out = 0;
+ return out - buffer;
+ case 3:
+ memcpy(out, &digits[0], 4), out += 4;
+ if ((digits[5] | digits[4]) != '0') {
+ *out++ = '.';
+ *out++ = digits[4];
+ if (digits[5] != '0') *out++ = digits[5];
+ }
+ *out = 0;
+ return out - buffer;
+ case 2:
+ memcpy(out, &digits[0], 3), out += 3;
+ *out++ = '.';
+ memcpy(out, &digits[3], 3);
+ out += 3;
+ while (out[-1] == '0') --out;
+ if (out[-1] == '.') --out;
+ *out = 0;
+ return out - buffer;
+ case 1:
+ memcpy(out, &digits[0], 2), out += 2;
+ *out++ = '.';
+ memcpy(out, &digits[2], 4);
+ out += 4;
+ while (out[-1] == '0') --out;
+ if (out[-1] == '.') --out;
+ *out = 0;
+ return out - buffer;
+ case 0:
+ memcpy(out, &digits[0], 1), out += 1;
+ *out++ = '.';
+ memcpy(out, &digits[1], 5);
+ out += 5;
+ while (out[-1] == '0') --out;
+ if (out[-1] == '.') --out;
+ *out = 0;
+ return out - buffer;
+ case -4:
+ out[2] = '0';
+ ++out;
+ ABSL_FALLTHROUGH_INTENDED;
+ case -3:
+ out[2] = '0';
+ ++out;
+ ABSL_FALLTHROUGH_INTENDED;
+ case -2:
+ out[2] = '0';
+ ++out;
+ ABSL_FALLTHROUGH_INTENDED;
+ case -1:
+ out += 2;
+ memcpy(out, &digits[0], 6);
+ out += 6;
+ while (out[-1] == '0') --out;
+ *out = 0;
+ return out - buffer;
+ }
+ assert(exp < -4 || exp >= 6);
+ out[0] = digits[0];
+ assert(out[1] == '.');
+ out += 2;
+ memcpy(out, &digits[1], 5), out += 5;
+ while (out[-1] == '0') --out;
+ if (out[-1] == '.') --out;
+ *out++ = 'e';
+ if (exp > 0) {
+ *out++ = '+';
+ } else {
+ *out++ = '-';
+ exp = -exp;
+ }
+ if (exp > 99) {
+ int dig1 = exp / 100;
+ exp -= dig1 * 100;
+ *out++ = '0' + dig1;
+ }
+ PutTwoDigits(exp, out);
+ out += 2;
+ *out = 0;
+ return out - buffer;
+}
+
+namespace {
+// Represents integer values of digits.
+// Uses 36 to indicate an invalid character since we support
+// bases up to 36.
+static const int8_t kAsciiToInt[256] = {
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, // 16 36s.
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 0, 1, 2, 3, 4, 5,
+ 6, 7, 8, 9, 36, 36, 36, 36, 36, 36, 36, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+ 36, 36, 36, 36, 36, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36};
+
+// Parse the sign and optional hex or oct prefix in text.
+inline bool safe_parse_sign_and_base(absl::string_view* text /*inout*/,
+ int* base_ptr /*inout*/,
+ bool* negative_ptr /*output*/) {
+ if (text->data() == nullptr) {
+ return false;
+ }
+
+ const char* start = text->data();
+ const char* end = start + text->size();
+ int base = *base_ptr;
+
+ // Consume whitespace.
+ while (start < end && absl::ascii_isspace(start[0])) {
+ ++start;
+ }
+ while (start < end && absl::ascii_isspace(end[-1])) {
+ --end;
+ }
+ if (start >= end) {
+ return false;
+ }
+
+ // Consume sign.
+ *negative_ptr = (start[0] == '-');
+ if (*negative_ptr || start[0] == '+') {
+ ++start;
+ if (start >= end) {
+ return false;
+ }
+ }
+
+ // Consume base-dependent prefix.
+ // base 0: "0x" -> base 16, "0" -> base 8, default -> base 10
+ // base 16: "0x" -> base 16
+ // Also validate the base.
+ if (base == 0) {
+ if (end - start >= 2 && start[0] == '0' &&
+ (start[1] == 'x' || start[1] == 'X')) {
+ base = 16;
+ start += 2;
+ if (start >= end) {
+ // "0x" with no digits after is invalid.
+ return false;
+ }
+ } else if (end - start >= 1 && start[0] == '0') {
+ base = 8;
+ start += 1;
+ } else {
+ base = 10;
+ }
+ } else if (base == 16) {
+ if (end - start >= 2 && start[0] == '0' &&
+ (start[1] == 'x' || start[1] == 'X')) {
+ start += 2;
+ if (start >= end) {
+ // "0x" with no digits after is invalid.
+ return false;
+ }
+ }
+ } else if (base >= 2 && base <= 36) {
+ // okay
+ } else {
+ return false;
+ }
+ *text = absl::string_view(start, end - start);
+ *base_ptr = base;
+ return true;
+}
+
+// Consume digits.
+//
+// The classic loop:
+//
+// for each digit
+// value = value * base + digit
+// value *= sign
+//
+// The classic loop needs overflow checking. It also fails on the most
+// negative integer, -2147483648 in 32-bit two's complement representation.
+//
+// My improved loop:
+//
+// if (!negative)
+// for each digit
+// value = value * base
+// value = value + digit
+// else
+// for each digit
+// value = value * base
+// value = value - digit
+//
+// Overflow checking becomes simple.
+
+// Lookup tables per IntType:
+// vmax/base and vmin/base are precomputed because division costs at least 8ns.
+// TODO(junyer): Doing this per base instead (i.e. an array of structs, not a
+// struct of arrays) would probably be better in terms of d-cache for the most
+// commonly used bases.
+template <typename IntType>
+struct LookupTables {
+ static const IntType kVmaxOverBase[];
+ static const IntType kVminOverBase[];
+};
+
+// An array initializer macro for X/base where base in [0, 36].
+// However, note that lookups for base in [0, 1] should never happen because
+// base has been validated to be in [2, 36] by safe_parse_sign_and_base().
+#define X_OVER_BASE_INITIALIZER(X) \
+ { \
+ 0, 0, X / 2, X / 3, X / 4, X / 5, X / 6, X / 7, X / 8, X / 9, X / 10, \
+ X / 11, X / 12, X / 13, X / 14, X / 15, X / 16, X / 17, X / 18, \
+ X / 19, X / 20, X / 21, X / 22, X / 23, X / 24, X / 25, X / 26, \
+ X / 27, X / 28, X / 29, X / 30, X / 31, X / 32, X / 33, X / 34, \
+ X / 35, X / 36, \
+ }
+
+template <typename IntType>
+const IntType LookupTables<IntType>::kVmaxOverBase[] =
+ X_OVER_BASE_INITIALIZER(std::numeric_limits<IntType>::max());
+
+template <typename IntType>
+const IntType LookupTables<IntType>::kVminOverBase[] =
+ X_OVER_BASE_INITIALIZER(std::numeric_limits<IntType>::min());
+
+#undef X_OVER_BASE_INITIALIZER
+
+template <typename IntType>
+inline bool safe_parse_positive_int(absl::string_view text, int base,
+ IntType* value_p) {
+ IntType value = 0;
+ const IntType vmax = std::numeric_limits<IntType>::max();
+ assert(vmax > 0);
+ assert(base >= 0);
+ assert(vmax >= static_cast<IntType>(base));
+ const IntType vmax_over_base = LookupTables<IntType>::kVmaxOverBase[base];
+ const char* start = text.data();
+ const char* end = start + text.size();
+ // loop over digits
+ for (; start < end; ++start) {
+ unsigned char c = static_cast<unsigned char>(start[0]);
+ int digit = kAsciiToInt[c];
+ if (digit >= base) {
+ *value_p = value;
+ return false;
+ }
+ if (value > vmax_over_base) {
+ *value_p = vmax;
+ return false;
+ }
+ value *= base;
+ if (value > vmax - digit) {
+ *value_p = vmax;
+ return false;
+ }
+ value += digit;
+ }
+ *value_p = value;
+ return true;
+}
+
+template <typename IntType>
+inline bool safe_parse_negative_int(absl::string_view text, int base,
+ IntType* value_p) {
+ IntType value = 0;
+ const IntType vmin = std::numeric_limits<IntType>::min();
+ assert(vmin < 0);
+ assert(vmin <= 0 - base);
+ IntType vmin_over_base = LookupTables<IntType>::kVminOverBase[base];
+ // 2003 c++ standard [expr.mul]
+ // "... the sign of the remainder is implementation-defined."
+ // Although (vmin/base)*base + vmin%base is always vmin.
+ // 2011 c++ standard tightens the spec but we cannot rely on it.
+ // TODO(junyer): Handle this in the lookup table generation.
+ if (vmin % base > 0) {
+ vmin_over_base += 1;
+ }
+ const char* start = text.data();
+ const char* end = start + text.size();
+ // loop over digits
+ for (; start < end; ++start) {
+ unsigned char c = static_cast<unsigned char>(start[0]);
+ int digit = kAsciiToInt[c];
+ if (digit >= base) {
+ *value_p = value;
+ return false;
+ }
+ if (value < vmin_over_base) {
+ *value_p = vmin;
+ return false;
+ }
+ value *= base;
+ if (value < vmin + digit) {
+ *value_p = vmin;
+ return false;
+ }
+ value -= digit;
+ }
+ *value_p = value;
+ return true;
+}
+
+// Input format based on POSIX.1-2008 strtol
+// http://pubs.opengroup.org/onlinepubs/9699919799/functions/strtol.html
+template <typename IntType>
+inline bool safe_int_internal(absl::string_view text, IntType* value_p,
+ int base) {
+ *value_p = 0;
+ bool negative;
+ if (!safe_parse_sign_and_base(&text, &base, &negative)) {
+ return false;
+ }
+ if (!negative) {
+ return safe_parse_positive_int(text, base, value_p);
+ } else {
+ return safe_parse_negative_int(text, base, value_p);
+ }
+}
+
+template <typename IntType>
+inline bool safe_uint_internal(absl::string_view text, IntType* value_p,
+ int base) {
+ *value_p = 0;
+ bool negative;
+ if (!safe_parse_sign_and_base(&text, &base, &negative) || negative) {
+ return false;
+ }
+ return safe_parse_positive_int(text, base, value_p);
+}
+} // anonymous namespace
+
+namespace numbers_internal {
+bool safe_strto32_base(absl::string_view text, int32_t* value, int base) {
+ return safe_int_internal<int32_t>(text, value, base);
+}
+
+bool safe_strto64_base(absl::string_view text, int64_t* value, int base) {
+ return safe_int_internal<int64_t>(text, value, base);
+}
+
+bool safe_strtou32_base(absl::string_view text, uint32_t* value, int base) {
+ return safe_uint_internal<uint32_t>(text, value, base);
+}
+
+bool safe_strtou64_base(absl::string_view text, uint64_t* value, int base) {
+ return safe_uint_internal<uint64_t>(text, value, base);
+}
+} // namespace numbers_internal
+
+} // namespace absl
--- /dev/null
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "s2/third_party/absl/strings/str_cat.h"
+
+#include <cassert>
+#include <algorithm>
+#include <cstdint>
+#include <cstring>
+
+#include "s2/third_party/absl/strings/ascii.h"
+#include "s2/third_party/absl/strings/internal/resize_uninitialized.h"
+
+namespace absl {
+
+AlphaNum::AlphaNum(Hex hex) {
+ char* const end = &digits_[numbers_internal::kFastToBufferSize];
+ char* writer = end;
+ uint64_t value = hex.value;
+ static const char hexdigits[] = "0123456789abcdef";
+ do {
+ *--writer = hexdigits[value & 0xF];
+ value >>= 4;
+ } while (value != 0);
+
+ char* beg;
+ if (end - writer < hex.width) {
+ beg = end - hex.width;
+ std::fill_n(beg, writer - beg, hex.fill);
+ } else {
+ beg = writer;
+ }
+
+ piece_ = absl::string_view(beg, end - beg);
+}
+
+AlphaNum::AlphaNum(Dec dec) {
+ assert(dec.width <= numbers_internal::kFastToBufferSize);
+ char* const end = &digits_[numbers_internal::kFastToBufferSize];
+ char* const minfill = end - dec.width;
+ char* writer = end;
+ uint64_t value = dec.value;
+ bool neg = dec.neg;
+ while (value > 9) {
+ *--writer = '0' + (value % 10);
+ value /= 10;
+ }
+ *--writer = '0' + value;
+ if (neg) *--writer = '-';
+
+ ptrdiff_t fillers = writer - minfill;
+ if (fillers > 0) {
+ // Tricky: if the fill character is ' ', then it's <fill><+/-><digits>
+ // But...: if the fill character is '0', then it's <+/-><fill><digits>
+ bool add_sign_again = false;
+ if (neg && dec.fill == '0') { // If filling with '0',
+ ++writer; // ignore the sign we just added
+ add_sign_again = true; // and re-add the sign later.
+ }
+ writer -= fillers;
+ std::fill_n(writer, fillers, dec.fill);
+ if (add_sign_again) *--writer = '-';
+ }
+
+ piece_ = absl::string_view(writer, end - writer);
+}
+
+// ----------------------------------------------------------------------
+// StrCat()
+// This merges the given strings or integers, with no delimiter. This
+// is designed to be the fastest possible way to construct a string out
+// of a mix of raw C strings, string_views, strings, and integer values.
+// ----------------------------------------------------------------------
+
+// Append is merely a version of memcpy that returns the address of the byte
+// after the area just overwritten.
+static char* Append(char* out, const AlphaNum& x) {
+ // memcpy is allowed to overwrite arbitrary memory, so doing this after the
+ // call would force an extra fetch of x.size().
+ char* after = out + x.size();
+ memcpy(out, x.data(), x.size());
+ return after;
+}
+
+string StrCat(const AlphaNum& a, const AlphaNum& b) {
+ string result;
+ absl::strings_internal::STLStringResizeUninitialized(&result,
+ a.size() + b.size());
+ char* const begin = &*result.begin();
+ char* out = begin;
+ out = Append(out, a);
+ out = Append(out, b);
+ assert(out == begin + result.size());
+ return result;
+}
+
+string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c) {
+ string result;
+ strings_internal::STLStringResizeUninitialized(
+ &result, a.size() + b.size() + c.size());
+ char* const begin = &*result.begin();
+ char* out = begin;
+ out = Append(out, a);
+ out = Append(out, b);
+ out = Append(out, c);
+ assert(out == begin + result.size());
+ return result;
+}
+
+string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c,
+ const AlphaNum& d) {
+ string result;
+ strings_internal::STLStringResizeUninitialized(
+ &result, a.size() + b.size() + c.size() + d.size());
+ char* const begin = &*result.begin();
+ char* out = begin;
+ out = Append(out, a);
+ out = Append(out, b);
+ out = Append(out, c);
+ out = Append(out, d);
+ assert(out == begin + result.size());
+ return result;
+}
+
+namespace strings_internal {
+
+// Do not call directly - these are not part of the public API.
+string CatPieces(std::initializer_list<absl::string_view> pieces) {
+ string result;
+ size_t total_size = 0;
+ for (const absl::string_view piece : pieces) total_size += piece.size();
+ strings_internal::STLStringResizeUninitialized(&result, total_size);
+
+ char* const begin = &*result.begin();
+ char* out = begin;
+ for (const absl::string_view piece : pieces) {
+ const size_t this_size = piece.size();
+ memcpy(out, piece.data(), this_size);
+ out += this_size;
+ }
+ assert(out == begin + result.size());
+ return result;
+}
+
+// It's possible to call StrAppend with an absl::string_view that is itself a
+// fragment of the string we're appending to. However the results of this are
+// random. Therefore, check for this in debug mode. Use unsigned math so we
+// only have to do one comparison. Note, there's an exception case: appending an
+// empty string is always allowed.
+#define ASSERT_NO_OVERLAP(dest, src) \
+ assert(((src).size() == 0) || \
+ (uintptr_t((src).data() - (dest).data()) > uintptr_t((dest).size())))
+
+void AppendPieces(string* dest,
+ std::initializer_list<absl::string_view> pieces) {
+ size_t old_size = dest->size();
+ size_t total_size = old_size;
+ for (const absl::string_view piece : pieces) {
+ ASSERT_NO_OVERLAP(*dest, piece);
+ total_size += piece.size();
+ }
+ strings_internal::STLStringResizeUninitialized(dest, total_size);
+
+ char* const begin = &*dest->begin();
+ char* out = begin + old_size;
+ for (const absl::string_view piece : pieces) {
+ const size_t this_size = piece.size();
+ memcpy(out, piece.data(), this_size);
+ out += this_size;
+ }
+ assert(out == begin + dest->size());
+}
+
+
+} // namespace strings_internal
+
+void StrAppend(string* dest, const AlphaNum& a) {
+ ASSERT_NO_OVERLAP(*dest, a);
+ dest->append(a.data(), a.size());
+}
+
+void StrAppend(string* dest, const AlphaNum& a, const AlphaNum& b) {
+ ASSERT_NO_OVERLAP(*dest, a);
+ ASSERT_NO_OVERLAP(*dest, b);
+ string::size_type old_size = dest->size();
+ strings_internal::STLStringResizeUninitialized(
+ dest, old_size + a.size() + b.size());
+ char* const begin = &*dest->begin();
+ char* out = begin + old_size;
+ out = Append(out, a);
+ out = Append(out, b);
+ assert(out == begin + dest->size());
+}
+
+void StrAppend(string* dest, const AlphaNum& a, const AlphaNum& b,
+ const AlphaNum& c) {
+ ASSERT_NO_OVERLAP(*dest, a);
+ ASSERT_NO_OVERLAP(*dest, b);
+ ASSERT_NO_OVERLAP(*dest, c);
+ string::size_type old_size = dest->size();
+ strings_internal::STLStringResizeUninitialized(
+ dest, old_size + a.size() + b.size() + c.size());
+ char* const begin = &*dest->begin();
+ char* out = begin + old_size;
+ out = Append(out, a);
+ out = Append(out, b);
+ out = Append(out, c);
+ assert(out == begin + dest->size());
+}
+
+void StrAppend(string* dest, const AlphaNum& a, const AlphaNum& b,
+ const AlphaNum& c, const AlphaNum& d) {
+ ASSERT_NO_OVERLAP(*dest, a);
+ ASSERT_NO_OVERLAP(*dest, b);
+ ASSERT_NO_OVERLAP(*dest, c);
+ ASSERT_NO_OVERLAP(*dest, d);
+ string::size_type old_size = dest->size();
+ strings_internal::STLStringResizeUninitialized(
+ dest, old_size + a.size() + b.size() + c.size() + d.size());
+ char* const begin = &*dest->begin();
+ char* out = begin + old_size;
+ out = Append(out, a);
+ out = Append(out, b);
+ out = Append(out, c);
+ out = Append(out, d);
+ assert(out == begin + dest->size());
+}
+
+} // namespace absl
--- /dev/null
+#include "s2/third_party/absl/strings/str_split.h"
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#include "s2/third_party/absl/strings/string_view.h"
+
+using absl::string_view;
+using std::function;
+using std::string;
+using std::vector;
+
+namespace absl {
+
+template <typename String>
+vector<String> StrSplit(String const& text, char const delim,
+ function<bool(string_view)> predicate) {
+ vector<String> elems;
+ typename String::size_type begin = 0;
+ typename String::size_type end;
+ while ((end = text.find(delim, begin)) != String::npos) {
+ string_view view(text.data() + begin, end - begin);
+ if (predicate(view))
+ elems.emplace_back(view);
+ begin = end + 1;
+ }
+ // Try to add the portion after the last delim.
+ string_view view(text.data() + begin, text.size() - begin);
+ if (predicate(view))
+ elems.emplace_back(view);
+ return elems;
+}
+template vector<string> StrSplit(string const& text, char const delim,
+ function<bool(string_view)> predicate);
+template vector<string_view> StrSplit(string_view const& text, char const delim,
+ function<bool(string_view)> predicate);
+
+template <typename String>
+vector<String> StrSplit(String const& text, char const delim) {
+ return StrSplit(text, delim, [](string_view) { return true; });
+}
+template vector<string> StrSplit(string const& text, char const delim);
+template vector<string_view> StrSplit(string_view const& text,
+ char const delim);
+
+} // namespace absl
--- /dev/null
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "s2/third_party/absl/strings/string_view.h"
+
+#ifndef ABSL_HAVE_STD_STRING_VIEW
+
+#include <algorithm>
+#include <climits>
+#include <cstring>
+#include <ostream>
+
+#include "s2/third_party/absl/strings/internal/memutil.h"
+
+namespace absl {
+
+namespace {
+void WritePadding(std::ostream& o, size_t pad) {
+ char fill_buf[32];
+ memset(fill_buf, o.fill(), sizeof(fill_buf));
+ while (pad) {
+ size_t n = std::min(pad, sizeof(fill_buf));
+ o.write(fill_buf, n);
+ pad -= n;
+ }
+}
+
+class LookupTable {
+ public:
+ // For each character in wanted, sets the index corresponding
+ // to the ASCII code of that character. This is used by
+ // the find_.*_of methods below to tell whether or not a character is in
+ // the lookup table in constant time.
+ explicit LookupTable(string_view wanted) {
+ for (char c : wanted) {
+ table_[Index(c)] = true;
+ }
+ }
+ bool operator[](char c) const { return table_[Index(c)]; }
+
+ private:
+ static unsigned char Index(char c) { return static_cast<unsigned char>(c); }
+ bool table_[UCHAR_MAX + 1] = {};
+};
+
+} // namespace
+
+std::ostream& operator<<(std::ostream& o, string_view piece) {
+ std::ostream::sentry sentry(o);
+ if (sentry) {
+ size_t lpad = 0;
+ size_t rpad = 0;
+ if (static_cast<size_t>(o.width()) > piece.size()) {
+ size_t pad = o.width() - piece.size();
+ if ((o.flags() & o.adjustfield) == o.left) {
+ rpad = pad;
+ } else {
+ lpad = pad;
+ }
+ }
+ if (lpad) WritePadding(o, lpad);
+ o.write(piece.data(), piece.size());
+ if (rpad) WritePadding(o, rpad);
+ o.width(0);
+ }
+ return o;
+}
+
+string_view::size_type string_view::copy(char* buf, size_type n,
+ size_type pos) const {
+ size_type ulen = length_;
+ assert(pos <= ulen);
+ size_type rlen = std::min(ulen - pos, n);
+ if (rlen > 0) {
+ const char* start = ptr_ + pos;
+ std::copy(start, start + rlen, buf);
+ }
+ return rlen;
+}
+
+string_view::size_type string_view::find(string_view s, size_type pos) const
+ noexcept {
+ if (empty() || pos > length_) {
+ if (empty() && pos == 0 && s.empty()) return 0;
+ return npos;
+ }
+ const char* result =
+ strings_internal::memmatch(ptr_ + pos, length_ - pos, s.ptr_, s.length_);
+ return result ? result - ptr_ : npos;
+}
+
+string_view::size_type string_view::find(char c, size_type pos) const noexcept {
+ if (empty() || pos >= length_) {
+ return npos;
+ }
+ const char* result =
+ static_cast<const char*>(memchr(ptr_ + pos, c, length_ - pos));
+ return result != nullptr ? result - ptr_ : npos;
+}
+
+string_view::size_type string_view::rfind(string_view s, size_type pos) const
+ noexcept {
+ if (length_ < s.length_) return npos;
+ if (s.empty()) return std::min(length_, pos);
+ const char* last = ptr_ + std::min(length_ - s.length_, pos) + s.length_;
+ const char* result = std::find_end(ptr_, last, s.ptr_, s.ptr_ + s.length_);
+ return result != last ? result - ptr_ : npos;
+}
+
+// Search range is [0..pos] inclusive. If pos == npos, search everything.
+string_view::size_type string_view::rfind(char c, size_type pos) const
+ noexcept {
+ // Note: memrchr() is not available on Windows.
+ if (empty()) return npos;
+ for (size_type i = std::min(pos, length_ - 1);; --i) {
+ if (ptr_[i] == c) {
+ return i;
+ }
+ if (i == 0) break;
+ }
+ return npos;
+}
+
+string_view::size_type string_view::find_first_of(string_view s,
+ size_type pos) const
+ noexcept {
+ if (empty() || s.empty()) {
+ return npos;
+ }
+ // Avoid the cost of LookupTable() for a single-character search.
+ if (s.length_ == 1) return find_first_of(s.ptr_[0], pos);
+ LookupTable tbl(s);
+ for (size_type i = pos; i < length_; ++i) {
+ if (tbl[ptr_[i]]) {
+ return i;
+ }
+ }
+ return npos;
+}
+
+string_view::size_type string_view::find_first_not_of(string_view s,
+ size_type pos) const
+ noexcept {
+ if (empty()) return npos;
+ // Avoid the cost of LookupTable() for a single-character search.
+ if (s.length_ == 1) return find_first_not_of(s.ptr_[0], pos);
+ LookupTable tbl(s);
+ for (size_type i = pos; i < length_; ++i) {
+ if (!tbl[ptr_[i]]) {
+ return i;
+ }
+ }
+ return npos;
+}
+
+string_view::size_type string_view::find_first_not_of(char c,
+ size_type pos) const
+ noexcept {
+ if (empty()) return npos;
+ for (; pos < length_; ++pos) {
+ if (ptr_[pos] != c) {
+ return pos;
+ }
+ }
+ return npos;
+}
+
+string_view::size_type string_view::find_last_of(string_view s,
+ size_type pos) const noexcept {
+ if (empty() || s.empty()) return npos;
+ // Avoid the cost of LookupTable() for a single-character search.
+ if (s.length_ == 1) return find_last_of(s.ptr_[0], pos);
+ LookupTable tbl(s);
+ for (size_type i = std::min(pos, length_ - 1);; --i) {
+ if (tbl[ptr_[i]]) {
+ return i;
+ }
+ if (i == 0) break;
+ }
+ return npos;
+}
+
+string_view::size_type string_view::find_last_not_of(string_view s,
+ size_type pos) const
+ noexcept {
+ if (empty()) return npos;
+ size_type i = std::min(pos, length_ - 1);
+ if (s.empty()) return i;
+ // Avoid the cost of LookupTable() for a single-character search.
+ if (s.length_ == 1) return find_last_not_of(s.ptr_[0], pos);
+ LookupTable tbl(s);
+ for (;; --i) {
+ if (!tbl[ptr_[i]]) {
+ return i;
+ }
+ if (i == 0) break;
+ }
+ return npos;
+}
+
+string_view::size_type string_view::find_last_not_of(char c,
+ size_type pos) const
+ noexcept {
+ if (empty()) return npos;
+ size_type i = std::min(pos, length_ - 1);
+ for (;; --i) {
+ if (ptr_[i] != c) {
+ return i;
+ }
+ if (i == 0) break;
+ }
+ return npos;
+}
+
+// MSVC has non-standard behavior that implicitly creates definitions for static
+// const members. These implicit definitions conflict with explicit out-of-class
+// member definitions that are required by the C++ standard, resulting in
+// LNK1169 "multiply defined" errors at link time. __declspec(selectany) asks
+// MSVC to choose only one definition for the symbol it decorates. See details
+// at http://msdn.microsoft.com/en-us/library/34h23df8(v=vs.100).aspx
+#ifdef _MSC_VER
+#define ABSL_STRING_VIEW_SELECTANY __declspec(selectany)
+#else
+#define ABSL_STRING_VIEW_SELECTANY
+#endif
+
+ABSL_STRING_VIEW_SELECTANY
+constexpr string_view::size_type string_view::npos;
+ABSL_STRING_VIEW_SELECTANY
+constexpr string_view::size_type string_view::kMaxSize;
+
+} // namespace absl
+
+#endif // ABSL_HAVE_STD_STRING_VIEW
--- /dev/null
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This file contains functions that remove a defined part from the string,
+// i.e., strip the string.
+
+#include "s2/third_party/absl/strings/strip.h"
+
+#include "s2/third_party/absl/strings/string_view.h"
+
+// ----------------------------------------------------------------------
+// ReplaceCharacters
+// Replaces any occurrence of any of the 'remove' *bytes*
+// with the 'replace_with' *byte*.
+// ----------------------------------------------------------------------
+void ReplaceCharacters(char* str, size_t len, absl::string_view remove,
+ char replace_with) {
+ for (char* end = str + len; str != end; ++str) {
+ if (remove.find(*str) != absl::string_view::npos) {
+ *str = replace_with;
+ }
+ }
+}
+
+void ReplaceCharacters(string* s, absl::string_view remove, char replace_with) {
+ for (char& ch : *s) {
+ if (remove.find(ch) != absl::string_view::npos) {
+ ch = replace_with;
+ }
+ }
+}
--- /dev/null
+// Copyright 2009 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: jyrki@google.com (Jyrki Alakuijala)
+//
+// Performance notes:
+// 2009-01-16: hendrie@google.com microbenchmarked InterleaveUint32 against
+// two table-free implementations from "Hacker's Delight" section 7-2.
+// This implementation was substantially faster: 8 ns vs 17 ns, even
+// without subtracting the time taken in the test harness itself.
+// Test environment: workstation w/ 2.4 GHz Core 2 Duo, compiler target
+// gcc-4.3.1-glibc-2.3.6-grte-k8.
+// TODO(user): Inlining InterleaveUint32 yields a measurable speedup (5
+// ns vs. 8 ns). Consider cost/benefit of moving implementations inline.
+
+#include "s2/util/bits/bit-interleave.h"
+
+#include "s2/base/integral_types.h"
+
+namespace util_bits {
+
+static const uint16 kInterleaveLut[256] = {
+ 0x0000, 0x0001, 0x0004, 0x0005, 0x0010, 0x0011, 0x0014, 0x0015,
+ 0x0040, 0x0041, 0x0044, 0x0045, 0x0050, 0x0051, 0x0054, 0x0055,
+ 0x0100, 0x0101, 0x0104, 0x0105, 0x0110, 0x0111, 0x0114, 0x0115,
+ 0x0140, 0x0141, 0x0144, 0x0145, 0x0150, 0x0151, 0x0154, 0x0155,
+ 0x0400, 0x0401, 0x0404, 0x0405, 0x0410, 0x0411, 0x0414, 0x0415,
+ 0x0440, 0x0441, 0x0444, 0x0445, 0x0450, 0x0451, 0x0454, 0x0455,
+ 0x0500, 0x0501, 0x0504, 0x0505, 0x0510, 0x0511, 0x0514, 0x0515,
+ 0x0540, 0x0541, 0x0544, 0x0545, 0x0550, 0x0551, 0x0554, 0x0555,
+
+ 0x1000, 0x1001, 0x1004, 0x1005, 0x1010, 0x1011, 0x1014, 0x1015,
+ 0x1040, 0x1041, 0x1044, 0x1045, 0x1050, 0x1051, 0x1054, 0x1055,
+ 0x1100, 0x1101, 0x1104, 0x1105, 0x1110, 0x1111, 0x1114, 0x1115,
+ 0x1140, 0x1141, 0x1144, 0x1145, 0x1150, 0x1151, 0x1154, 0x1155,
+ 0x1400, 0x1401, 0x1404, 0x1405, 0x1410, 0x1411, 0x1414, 0x1415,
+ 0x1440, 0x1441, 0x1444, 0x1445, 0x1450, 0x1451, 0x1454, 0x1455,
+ 0x1500, 0x1501, 0x1504, 0x1505, 0x1510, 0x1511, 0x1514, 0x1515,
+ 0x1540, 0x1541, 0x1544, 0x1545, 0x1550, 0x1551, 0x1554, 0x1555,
+
+ 0x4000, 0x4001, 0x4004, 0x4005, 0x4010, 0x4011, 0x4014, 0x4015,
+ 0x4040, 0x4041, 0x4044, 0x4045, 0x4050, 0x4051, 0x4054, 0x4055,
+ 0x4100, 0x4101, 0x4104, 0x4105, 0x4110, 0x4111, 0x4114, 0x4115,
+ 0x4140, 0x4141, 0x4144, 0x4145, 0x4150, 0x4151, 0x4154, 0x4155,
+ 0x4400, 0x4401, 0x4404, 0x4405, 0x4410, 0x4411, 0x4414, 0x4415,
+ 0x4440, 0x4441, 0x4444, 0x4445, 0x4450, 0x4451, 0x4454, 0x4455,
+ 0x4500, 0x4501, 0x4504, 0x4505, 0x4510, 0x4511, 0x4514, 0x4515,
+ 0x4540, 0x4541, 0x4544, 0x4545, 0x4550, 0x4551, 0x4554, 0x4555,
+
+ 0x5000, 0x5001, 0x5004, 0x5005, 0x5010, 0x5011, 0x5014, 0x5015,
+ 0x5040, 0x5041, 0x5044, 0x5045, 0x5050, 0x5051, 0x5054, 0x5055,
+ 0x5100, 0x5101, 0x5104, 0x5105, 0x5110, 0x5111, 0x5114, 0x5115,
+ 0x5140, 0x5141, 0x5144, 0x5145, 0x5150, 0x5151, 0x5154, 0x5155,
+ 0x5400, 0x5401, 0x5404, 0x5405, 0x5410, 0x5411, 0x5414, 0x5415,
+ 0x5440, 0x5441, 0x5444, 0x5445, 0x5450, 0x5451, 0x5454, 0x5455,
+ 0x5500, 0x5501, 0x5504, 0x5505, 0x5510, 0x5511, 0x5514, 0x5515,
+ 0x5540, 0x5541, 0x5544, 0x5545, 0x5550, 0x5551, 0x5554, 0x5555,
+};
+
+uint16 InterleaveUint8(const uint8 val0, const uint8 val1) {
+ return kInterleaveLut[val0] | (kInterleaveLut[val1] << 1);
+}
+
+uint32 InterleaveUint16(const uint16 val0, const uint16 val1) {
+ return kInterleaveLut[val0 & 0xff] |
+ (kInterleaveLut[val0 >> 8] << 16) |
+ (kInterleaveLut[val1 & 0xff] << 1) |
+ (kInterleaveLut[val1 >> 8] << 17);
+}
+
+uint64 InterleaveUint32(const uint32 val0, const uint32 val1) {
+ return
+ (static_cast<uint64>(kInterleaveLut[val0 & 0xff])) |
+ (static_cast<uint64>(kInterleaveLut[(val0 >> 8) & 0xff]) << 16) |
+ (static_cast<uint64>(kInterleaveLut[(val0 >> 16) & 0xff]) << 32) |
+ (static_cast<uint64>(kInterleaveLut[val0 >> 24]) << 48) |
+ (static_cast<uint64>(kInterleaveLut[val1 & 0xff]) << 1) |
+ (static_cast<uint64>(kInterleaveLut[(val1 >> 8) & 0xff]) << 17) |
+ (static_cast<uint64>(kInterleaveLut[(val1 >> 16) & 0xff]) << 33) |
+ (static_cast<uint64>(kInterleaveLut[val1 >> 24]) << 49);
+}
+
+// The lookup table below can convert a sequence of interleaved 8 bits into
+// non-interleaved 4 bits. The table can convert both odd and even bits at the
+// same time, and lut[x & 0x55] converts the even bits (bits 0, 2, 4 and 6),
+// while lut[x & 0xaa] converts the odd bits (bits 1, 3, 5 and 7).
+//
+// The lookup table below was generated using the following python code:
+//
+// def deinterleave(bits):
+// if bits == 0: return 0
+// if bits < 4: return 1
+// return deinterleave(bits / 4) * 2 + deinterleave(bits & 3)
+//
+// for i in range(256): print "0x%x," % deinterleave(i),
+//
+static const uint8 kDeinterleaveLut[256] = {
+ 0x0, 0x1, 0x1, 0x1, 0x2, 0x3, 0x3, 0x3,
+ 0x2, 0x3, 0x3, 0x3, 0x2, 0x3, 0x3, 0x3,
+ 0x4, 0x5, 0x5, 0x5, 0x6, 0x7, 0x7, 0x7,
+ 0x6, 0x7, 0x7, 0x7, 0x6, 0x7, 0x7, 0x7,
+ 0x4, 0x5, 0x5, 0x5, 0x6, 0x7, 0x7, 0x7,
+ 0x6, 0x7, 0x7, 0x7, 0x6, 0x7, 0x7, 0x7,
+ 0x4, 0x5, 0x5, 0x5, 0x6, 0x7, 0x7, 0x7,
+ 0x6, 0x7, 0x7, 0x7, 0x6, 0x7, 0x7, 0x7,
+
+ 0x8, 0x9, 0x9, 0x9, 0xa, 0xb, 0xb, 0xb,
+ 0xa, 0xb, 0xb, 0xb, 0xa, 0xb, 0xb, 0xb,
+ 0xc, 0xd, 0xd, 0xd, 0xe, 0xf, 0xf, 0xf,
+ 0xe, 0xf, 0xf, 0xf, 0xe, 0xf, 0xf, 0xf,
+ 0xc, 0xd, 0xd, 0xd, 0xe, 0xf, 0xf, 0xf,
+ 0xe, 0xf, 0xf, 0xf, 0xe, 0xf, 0xf, 0xf,
+ 0xc, 0xd, 0xd, 0xd, 0xe, 0xf, 0xf, 0xf,
+ 0xe, 0xf, 0xf, 0xf, 0xe, 0xf, 0xf, 0xf,
+
+ 0x8, 0x9, 0x9, 0x9, 0xa, 0xb, 0xb, 0xb,
+ 0xa, 0xb, 0xb, 0xb, 0xa, 0xb, 0xb, 0xb,
+ 0xc, 0xd, 0xd, 0xd, 0xe, 0xf, 0xf, 0xf,
+ 0xe, 0xf, 0xf, 0xf, 0xe, 0xf, 0xf, 0xf,
+ 0xc, 0xd, 0xd, 0xd, 0xe, 0xf, 0xf, 0xf,
+ 0xe, 0xf, 0xf, 0xf, 0xe, 0xf, 0xf, 0xf,
+ 0xc, 0xd, 0xd, 0xd, 0xe, 0xf, 0xf, 0xf,
+ 0xe, 0xf, 0xf, 0xf, 0xe, 0xf, 0xf, 0xf,
+
+ 0x8, 0x9, 0x9, 0x9, 0xa, 0xb, 0xb, 0xb,
+ 0xa, 0xb, 0xb, 0xb, 0xa, 0xb, 0xb, 0xb,
+ 0xc, 0xd, 0xd, 0xd, 0xe, 0xf, 0xf, 0xf,
+ 0xe, 0xf, 0xf, 0xf, 0xe, 0xf, 0xf, 0xf,
+ 0xc, 0xd, 0xd, 0xd, 0xe, 0xf, 0xf, 0xf,
+ 0xe, 0xf, 0xf, 0xf, 0xe, 0xf, 0xf, 0xf,
+ 0xc, 0xd, 0xd, 0xd, 0xe, 0xf, 0xf, 0xf,
+ 0xe, 0xf, 0xf, 0xf, 0xe, 0xf, 0xf, 0xf,
+};
+
+void DeinterleaveUint8(uint16 val, uint8 *val0, uint8 *val1) {
+ *val0 = ((kDeinterleaveLut[val & 0x55]) |
+ (kDeinterleaveLut[(val >> 8) & 0x55] << 4));
+ *val1 = ((kDeinterleaveLut[val & 0xaa]) |
+ (kDeinterleaveLut[(val >> 8) & 0xaa] << 4));
+}
+
+void DeinterleaveUint16(uint32 code, uint16 *val0, uint16 *val1) {
+ *val0 = ((kDeinterleaveLut[code & 0x55]) |
+ (kDeinterleaveLut[(code >> 8) & 0x55] << 4) |
+ (kDeinterleaveLut[(code >> 16) & 0x55] << 8) |
+ (kDeinterleaveLut[(code >> 24) & 0x55] << 12));
+ *val1 = ((kDeinterleaveLut[code & 0xaa]) |
+ (kDeinterleaveLut[(code >> 8) & 0xaa] << 4) |
+ (kDeinterleaveLut[(code >> 16) & 0xaa] << 8) |
+ (kDeinterleaveLut[(code >> 24) & 0xaa] << 12));
+}
+
+void DeinterleaveUint32(uint64 code, uint32 *val0, uint32 *val1) {
+ *val0 = ((kDeinterleaveLut[code & 0x55]) |
+ (kDeinterleaveLut[(code >> 8) & 0x55] << 4) |
+ (kDeinterleaveLut[(code >> 16) & 0x55] << 8) |
+ (kDeinterleaveLut[(code >> 24) & 0x55] << 12) |
+ (kDeinterleaveLut[(code >> 32) & 0x55] << 16) |
+ (kDeinterleaveLut[(code >> 40) & 0x55] << 20) |
+ (kDeinterleaveLut[(code >> 48) & 0x55] << 24) |
+ (kDeinterleaveLut[(code >> 56) & 0x55] << 28));
+ *val1 = ((kDeinterleaveLut[code & 0xaa]) |
+ (kDeinterleaveLut[(code >> 8) & 0xaa] << 4) |
+ (kDeinterleaveLut[(code >> 16) & 0xaa] << 8) |
+ (kDeinterleaveLut[(code >> 24) & 0xaa] << 12) |
+ (kDeinterleaveLut[(code >> 32) & 0xaa] << 16) |
+ (kDeinterleaveLut[(code >> 40) & 0xaa] << 20) |
+ (kDeinterleaveLut[(code >> 48) & 0xaa] << 24) |
+ (kDeinterleaveLut[(code >> 56) & 0xaa] << 28));
+}
+
+// Derivation of the multiplication based interleave algorithm:
+// 1. Original value, bit positions shown:
+// x = --------------------------------------------------------87654321
+// 2. Replicate the byte and select the bits we want:
+// * 0b0000000100000001000000010000000100000001000000010000000100000001
+// 0x 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
+// => 8765432187654321876543218765432187654321876543218765432187654321
+// & 0b0000000000000000000000001100000000001100000000000011000000000011
+// 0x 0 0 0 0 0 0 C 0 0 C 0 0 3 0 0 3
+// => ------------------------87----------43------------65----------21
+// 3. Use multiplication to perform additions at different offsets:
+// * 0b0000000000000000000000000000000000000000010100000000000000000101
+// 0x 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 5
+// =>
+// ------------------------87----------43------------65----------21
+// + ----------------------87----------43------------65----------21
+// + ----87----------43------------65----------21
+// + --87----------43------------65----------21
+// => --8787--------4343----8787--6565--4343--2121----6565--------2121
+// 4. Notice the bits never collide. Select the bits we want.
+// & 0b0000000000000000000000100100100100100100100100000000000000000000
+// 0x 0 0 0 0 0 2 4 9 2 4 9 0 0 0 0 0
+// => ----------------------8--7--6--5--4--3--2--1--------------------
+// 5. To produce the final result, we bitwise OR the 3 split integers and shift
+// back to the LSB. To save instructions, we use template<N> to have steps
+// 3 and 4 produce results shifted over N bits.
+// Benchmark Time(ns) CPU(ns) Iterations
+// -------------------------------------------------------------
+// BM_3_InterleaveUint8 5 5 141967960
+// BM_3_ReferenceBitInterleave3 58 58 10000000
+// BM_3_InterleaveUint8_NoTemplate 11 11 61082024
+template<int kShift>
+static uint64 SplitFor3(uint8 x) {
+ return
+ ((((x * 0x0101010101010101ULL)
+ & 0x000000C00C003003ULL)
+ * (0x0000000000500005ULL << kShift))
+ & (0x0000024924900000ULL << kShift));
+}
+
+uint32 InterleaveUint8(uint8 val0, uint8 val1, uint8 val2) {
+ return static_cast<uint32>(
+ (SplitFor3<0>(val0) | SplitFor3<1>(val1) | SplitFor3<2>(val2)) >> 20);
+}
+
+// Multiplication based de-interleave algorithm:
+// 1. Original value, bit positions shown:
+// --------xx8xx7xx6xx5xx4xx3xx2xx1
+// & 0x 0 0 2 4 9 2 4 9
+// => ----------8--7--6--5--4--3--2--1
+// 2. Use multiplication to perform additions at different offsets:
+// * 0b00000000000000000000000000010101
+// 0x 0 0 0 0 0 0 1 5
+// =>
+// ----------8--7--6--5--4--3--2--1
+// + --------8--7--6--5--4--3--2--1
+// + ------8--7--6--5--4--3--2--1
+// => ------8-8787676565454343232121-1
+// 3. Select the bits we need
+// & 0b00000000001110000001110000001100
+// 0x 0 0 3 8 1 C 0 C
+// => ----------876------543------21--
+// 4. Multiply again to make the additions we need
+// * 0b00000000000000000001000001000001
+// 0x 0 0 0 0 1 0 4 1
+// =>
+// ----------876------543------21--
+// + ----876------543------21--
+// + 6------543------21--
+// => ----------87654321--------------
+// 5. Shift down so it lives at the lower 8 bits.
+// Benchmark Time(ns) CPU(ns) Iterations
+// ----------------------------------------------------------------
+// BM_3_DeinterleaveUint8 8 8 88136788
+// BM_3_DeinterleaveUint8_Using_Template 10 10 67385445
+// BM_3_DeinterleaveUint8_Uint64_Param 10 10 70838731
+// BM_3_ReferenceDeinterleaveUint8 79 79 8712211
+static inline uint8 UnsplitFor3(uint32 x) {
+ return ((((x & 0x00249249U)
+ * 0x00000015U)
+ & 0x00381C0CU)
+ * 0x00001041U) >> 14;
+}
+
+void DeinterleaveUint8(uint32 x, uint8* a, uint8* b, uint8* c) {
+ *a = UnsplitFor3(x);
+ *b = UnsplitFor3(x >> 1);
+ *c = UnsplitFor3(x >> 2);
+}
+
+} // namespace util_bits
--- /dev/null
+// Copyright 2002 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+//
+// Derived from code by Moses Charikar
+
+#include "s2/util/bits/bits.h"
+
+#include <cassert>
+#include "s2/third_party/absl/numeric/int128.h"
+
+using absl::uint128;
+
+// this array gives the number of bits for any number from 0 to 255
+// (We could make these ints. The tradeoff is size (eg does it overwhelm
+// the cache?) vs efficiency in referencing sub-word-sized array elements)
+const char Bits::num_bits[] = {
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 };
+
+int Bits::Count(const void *m, int num_bytes) {
+ int nbits = 0;
+ const uint8 *s = (const uint8 *) m;
+ for (int i = 0; i < num_bytes; i++)
+ nbits += num_bits[*s++];
+ return nbits;
+}
+
+int Bits::Difference(const void *m1, const void *m2, int num_bytes) {
+ int nbits = 0;
+ const uint8 *s1 = (const uint8 *) m1;
+ const uint8 *s2 = (const uint8 *) m2;
+ for (int i = 0; i < num_bytes; i++)
+ nbits += num_bits[(*s1++) ^ (*s2++)];
+ return nbits;
+}
+
+int Bits::CappedDifference(const void *m1, const void *m2,
+ int num_bytes, int cap) {
+ int nbits = 0;
+ const uint8 *s1 = (const uint8 *) m1;
+ const uint8 *s2 = (const uint8 *) m2;
+ for (int i = 0; i < num_bytes && nbits <= cap; i++)
+ nbits += num_bits[(*s1++) ^ (*s2++)];
+ return nbits;
+}
+
+int Bits::Log2Floor_Portable(uint32 n) {
+ if (n == 0)
+ return -1;
+ int log = 0;
+ uint32 value = n;
+ for (int i = 4; i >= 0; --i) {
+ int shift = (1 << i);
+ uint32 x = value >> shift;
+ if (x != 0) {
+ value = x;
+ log += shift;
+ }
+ }
+ assert(value == 1);
+ return log;
+}
+
+int Bits::Log2Ceiling(uint32 n) {
+ int floor = Log2Floor(n);
+ if ((n & (n - 1)) == 0) // zero or a power of two
+ return floor;
+ else
+ return floor + 1;
+}
+
+int Bits::Log2Ceiling64(uint64 n) {
+ int floor = Log2Floor64(n);
+ if ((n & (n - 1)) == 0) // zero or a power of two
+ return floor;
+ else
+ return floor + 1;
+}
+
+int Bits::Log2Ceiling128(absl::uint128 n) {
+ int floor = Log2Floor128(n);
+ if ((n & (n - 1)) == 0) // zero or a power of two
+ return floor;
+ else
+ return floor + 1;
+}
+
+int Bits::FindLSBSetNonZero_Portable(uint32 n) {
+ int rc = 31;
+ for (int i = 4, shift = 1 << 4; i >= 0; --i) {
+ const uint32 x = n << shift;
+ if (x != 0) {
+ n = x;
+ rc -= shift;
+ }
+ shift >>= 1;
+ }
+ return rc;
+}
+
+int Bits::CountLeadingZeros32_Portable(uint32 n) {
+ int bits = 1;
+ if (n == 0)
+ return 32;
+ if ((n >> 16) == 0) {
+ bits += 16;
+ n <<= 16;
+ }
+ if ((n >> 24) == 0) {
+ bits += 8;
+ n <<= 8;
+ }
+ if ((n >> 28) == 0) {
+ bits += 4;
+ n <<= 4;
+ }
+ if ((n >> 30) == 0) {
+ bits += 2;
+ n <<= 2;
+ }
+ return bits - (n >> 31);
+}
+
+int Bits::CountLeadingZeros64_Portable(uint64 n) {
+ return ((n >> 32)
+ ? Bits::CountLeadingZeros32_Portable(n >> 32)
+ : 32 + Bits::CountLeadingZeros32_Portable(n));
+}
--- /dev/null
+// Copyright 2000 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+//
+//
+
+#include "s2/util/coding/coder.h"
+
+#include <algorithm>
+#include <cassert>
+
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+
+// An initialization value used when we are allowed to
+unsigned char Encoder::kEmptyBuffer = 0;
+
+Encoder::Encoder()
+ : underlying_buffer_(&kEmptyBuffer) {
+}
+
+Encoder::~Encoder() {
+ S2_CHECK_LE(buf_, limit_); // Catch the buffer overflow.
+ if (underlying_buffer_ != &kEmptyBuffer) {
+ std::allocator<unsigned char>().deallocate(
+ underlying_buffer_, limit_ - orig_);
+ }
+}
+
+int Encoder::varint32_length(uint32 v) {
+ return Varint::Length32(v);
+}
+
+int Encoder::varint64_length(uint64 v) {
+ return Varint::Length64(v);
+}
+
+void Encoder::EnsureSlowPath(size_t N) {
+ S2_CHECK(ensure_allowed());
+ assert(avail() < N);
+ assert(length() == 0 || orig_ == underlying_buffer_);
+
+ // Double buffer size, but make sure we always have at least N extra bytes
+ const size_t current_len = length();
+ const size_t new_capacity = std::max(current_len + N, 2 * current_len);
+
+ unsigned char* new_buffer = std::allocator<unsigned char>().allocate(
+ new_capacity);
+ memcpy(new_buffer, underlying_buffer_, current_len);
+ if (underlying_buffer_ != &kEmptyBuffer) {
+ std::allocator<unsigned char>().deallocate(
+ underlying_buffer_, limit_ - orig_);
+ }
+ underlying_buffer_ = new_buffer;
+
+ orig_ = new_buffer;
+ limit_ = new_buffer + new_capacity;
+ buf_ = orig_ + current_len;
+ S2_CHECK(avail() >= N);
+}
+
+void Encoder::RemoveLast(size_t N) {
+ S2_CHECK(length() >= N);
+ buf_ -= N;
+}
+
+void Encoder::Resize(size_t N) {
+ S2_CHECK(length() >= N);
+ buf_ = orig_ + N;
+ assert(length() == N);
+}
--- /dev/null
+// Copyright 2001 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+
+#include "s2/util/coding/varint.h"
+
+#include <string>
+
+#include "s2/base/integral_types.h"
+
+#ifndef _MSC_VER
+const int Varint::kMax32;
+const int Varint::kMax64;
+const int Varint::kSlopBytes;
+#endif
+
+char* Varint::Encode32(char* sptr, uint32 v) {
+ return Encode32Inline(sptr, v);
+}
+
+char* Varint::Encode64(char* sptr, uint64 v) {
+ if (v < (1u << 28)) {
+ return Varint::Encode32(sptr, v);
+ } else {
+ // Operate on characters as unsigneds
+ unsigned char* ptr = reinterpret_cast<unsigned char*>(sptr);
+ // Rather than computing four subresults and or'ing each with 0x80,
+ // we can do two ors now. (Doing one now wouldn't work.)
+ const uint32 x32 = v | (1 << 7) | (1 << 21);
+ const uint32 y32 = v | (1 << 14) | (1 << 28);
+ *(ptr++) = x32;
+ *(ptr++) = y32 >> 7;
+ *(ptr++) = x32 >> 14;
+ *(ptr++) = y32 >> 21;
+ if (v < (1ull << 35)) {
+ *(ptr++) = v >> 28;
+ return reinterpret_cast<char*>(ptr);
+ } else {
+ *(ptr++) = (v >> 28) | (1 << 7);
+ return Varint::Encode32(reinterpret_cast<char*>(ptr), v >> 35);
+ }
+ }
+}
+
+const char* Varint::Parse32Fallback(const char* ptr, uint32* OUTPUT) {
+ return Parse32FallbackInline(ptr, OUTPUT);
+}
+
+const char* Varint::Parse64Fallback(const char* p, uint64* OUTPUT) {
+ const unsigned char* ptr = reinterpret_cast<const unsigned char*>(p);
+ assert(*ptr >= 128);
+#if defined(__x86_64__)
+ // This approach saves one redundant operation on the last byte (masking a
+ // byte that doesn't need it). This is conditional on x86 because:
+ // - PowerPC has specialized bit instructions that make masking and
+ // shifting very efficient
+ // - x86 seems to be one of the few architectures that has a single
+ // instruction to add 3 values.
+ //
+ // e.g.
+ // Input: 0xff, 0x40
+ // Mask & Or calculates: (0xff & 0x7f) | ((0x40 & 0x7f) << 7) = 0x207f
+ // Sub1 & Add calculates: 0xff + ((0x40 - 1) << 7) = 0x207f
+ //
+ // The subtract one removes the bit set by the previous byte used to
+ // indicate that more bytes are present. It also has the potential to
+ // allow instructions like LEA to combine 2 adds into one instruction.
+ //
+ // E.g. on an x86 architecture, %rcx = %rax + (%rbx - 1) << 7 could be
+ // emitted as:
+ // shlq $7, %rbx
+ // leaq -0x80(%rax, %rbx), %rcx
+ //
+ // Fast path: need to accumulate data in upto three result fragments
+ // res1 bits 0..27
+ // res2 bits 28..55
+ // res3 bits 56..63
+
+ uint64 byte, res1, res2 = 0, res3 = 0;
+ byte = *(ptr++); res1 = byte;
+ byte = *(ptr++); res1 += (byte - 1) << 7; if (byte < 128) goto done1;
+ byte = *(ptr++); res1 += (byte - 1) << 14; if (byte < 128) goto done1;
+ byte = *(ptr++); res1 += (byte - 1) << 21; if (byte < 128) goto done1;
+
+ byte = *(ptr++); res2 = byte; if (byte < 128) goto done2;
+ byte = *(ptr++); res2 += (byte - 1) << 7; if (byte < 128) goto done2;
+ byte = *(ptr++); res2 += (byte - 1) << 14; if (byte < 128) goto done2;
+ byte = *(ptr++); res2 += (byte - 1) << 21; if (byte < 128) goto done2;
+
+ byte = *(ptr++); res3 = byte; if (byte < 128) goto done3;
+ byte = *(ptr++); res3 += (byte - 1) << 7; if (byte < 2) goto done3;
+
+ return nullptr; // Value is too long to be a varint64
+
+ done1:
+ assert(res2 == 0);
+ assert(res3 == 0);
+ *OUTPUT = res1;
+ return reinterpret_cast<const char*>(ptr);
+
+ done2:
+ assert(res3 == 0);
+ *OUTPUT = res1 + ((res2 - 1) << 28);
+ return reinterpret_cast<const char*>(ptr);
+
+ done3:
+ *OUTPUT = res1 + ((res2 - 1) << 28) + ((res3 - 1) << 56);
+ return reinterpret_cast<const char*>(ptr);
+#else
+ uint32 byte, res1, res2=0, res3=0;
+ byte = *(ptr++); res1 = byte & 127;
+ byte = *(ptr++); res1 |= (byte & 127) << 7; if (byte < 128) goto done1;
+ byte = *(ptr++); res1 |= (byte & 127) << 14; if (byte < 128) goto done1;
+ byte = *(ptr++); res1 |= (byte & 127) << 21; if (byte < 128) goto done1;
+
+ byte = *(ptr++); res2 = byte & 127; if (byte < 128) goto done2;
+ byte = *(ptr++); res2 |= (byte & 127) << 7; if (byte < 128) goto done2;
+ byte = *(ptr++); res2 |= (byte & 127) << 14; if (byte < 128) goto done2;
+ byte = *(ptr++); res2 |= (byte & 127) << 21; if (byte < 128) goto done2;
+
+ byte = *(ptr++); res3 = byte & 127; if (byte < 128) goto done3;
+ byte = *(ptr++); res3 |= (byte & 127) << 7; if (byte < 2) goto done3;
+
+ return nullptr; // Value is too long to be a varint64
+
+ done1:
+ assert(res2 == 0);
+ assert(res3 == 0);
+ *OUTPUT = res1;
+ return reinterpret_cast<const char*>(ptr);
+
+ done2:
+ assert(res3 == 0);
+ *OUTPUT = res1 | (uint64(res2) << 28);
+ return reinterpret_cast<const char*>(ptr);
+
+ done3:
+ *OUTPUT = res1 | (uint64(res2) << 28) | (uint64(res3) << 56);
+ return reinterpret_cast<const char*>(ptr);
+#endif
+}
+
+const char* Varint::Parse32BackwardSlow(const char* ptr, const char* base,
+ uint32* OUTPUT) {
+ // Since this method is rarely called, for simplicity, we just skip backward
+ // and then parse forward.
+ const char* prev = Skip32BackwardSlow(ptr, base);
+ if (prev == nullptr)
+ return nullptr; // no value before 'ptr'
+
+ Parse32(prev, OUTPUT);
+ return prev;
+}
+
+const char* Varint::Parse64BackwardSlow(const char* ptr, const char* base,
+ uint64* OUTPUT) {
+ // Since this method is rarely called, for simplicity, we just skip backward
+ // and then parse forward.
+ const char* prev = Skip64BackwardSlow(ptr, base);
+ if (prev == nullptr)
+ return nullptr; // no value before 'ptr'
+
+ Parse64(prev, OUTPUT);
+ return prev;
+}
+
+const char* Varint::Parse64WithLimit(const char* p,
+ const char* l,
+ uint64* OUTPUT) {
+ if (p + kMax64 <= l) {
+ return Parse64(p, OUTPUT);
+ } else {
+ // See detailed comment in Varint::Parse64Fallback about this general
+ // approach.
+ const unsigned char* ptr = reinterpret_cast<const unsigned char*>(p);
+ const unsigned char* limit = reinterpret_cast<const unsigned char*>(l);
+ uint64 b, result;
+#if defined(__x86_64__)
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result = b; if (b < 128) goto done;
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result += (b - 1) << 7; if (b < 128) goto done;
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result += (b - 1) << 14; if (b < 128) goto done;
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result += (b - 1) << 21; if (b < 128) goto done;
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result += (b - 1) << 28; if (b < 128) goto done;
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result += (b - 1) << 35; if (b < 128) goto done;
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result += (b - 1) << 42; if (b < 128) goto done;
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result += (b - 1) << 49; if (b < 128) goto done;
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result += (b - 1) << 56; if (b < 128) goto done;
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result += (b - 1) << 63; if (b < 2) goto done;
+ return nullptr; // Value is too long to be a varint64
+#else
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result = b & 127; if (b < 128) goto done;
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result |= (b & 127) << 7; if (b < 128) goto done;
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result |= (b & 127) << 14; if (b < 128) goto done;
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result |= (b & 127) << 21; if (b < 128) goto done;
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result |= (b & 127) << 28; if (b < 128) goto done;
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result |= (b & 127) << 35; if (b < 128) goto done;
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result |= (b & 127) << 42; if (b < 128) goto done;
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result |= (b & 127) << 49; if (b < 128) goto done;
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result |= (b & 127) << 56; if (b < 128) goto done;
+ if (ptr >= limit) return nullptr;
+ b = *(ptr++); result |= (b & 127) << 63; if (b < 2) goto done;
+ return nullptr; // Value is too long to be a varint64
+#endif
+ done:
+ *OUTPUT = result;
+ return reinterpret_cast<const char*>(ptr);
+ }
+}
+
+const char* Varint::Skip32BackwardSlow(const char* p, const char* b) {
+ const unsigned char* ptr = reinterpret_cast<const unsigned char*>(p);
+ const unsigned char* base = reinterpret_cast<const unsigned char*>(b);
+ assert(ptr >= base);
+
+ // If the initial pointer is at the base or if the previous byte is not
+ // the last byte of a varint, we return nullptr since there is nothing to
+ // skip.
+ if (ptr == base) return nullptr;
+ if (*(--ptr) > 127) return nullptr;
+
+ for (int i = 0; i < 5; i++) {
+ if (ptr == base) return reinterpret_cast<const char*>(ptr);
+ if (*(--ptr) < 128) return reinterpret_cast<const char*>(ptr + 1);
+ }
+
+ return nullptr; // value is too long to be a varint32
+}
+
+const char* Varint::Skip64BackwardSlow(const char* p, const char* b) {
+ const unsigned char* ptr = reinterpret_cast<const unsigned char*>(p);
+ const unsigned char* base = reinterpret_cast<const unsigned char*>(b);
+ assert(ptr >= base);
+
+ // If the initial pointer is at the base or if the previous byte is not
+ // the last byte of a varint, we return nullptr since there is nothing to
+ // skip.
+ if (ptr == base) return nullptr;
+ if (*(--ptr) > 127) return nullptr;
+
+ for (int i = 0; i < 10; i++) {
+ if (ptr == base) return reinterpret_cast<const char*>(ptr);
+ if (*(--ptr) < 128) return reinterpret_cast<const char*>(ptr + 1);
+ }
+
+ return nullptr; // value is too long to be a varint64
+}
+
+void Varint::Append32Slow(string* s, uint32 value) {
+ const size_t start = s->size();
+ s->resize(start + Varint::Length32(value));
+ Varint::Encode32(&((*s)[start]), value);
+}
+
+void Varint::Append64Slow(string* s, uint64 value) {
+ const size_t start = s->size();
+ s->resize(start + Varint::Length64(value));
+ Varint::Encode64(&((*s)[start]), value);
+}
--- /dev/null
+// Copyright 2009 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/util/math/exactfloat/exactfloat.h"
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <algorithm>
+#include <cmath>
+#include <limits>
+
+#include <openssl/bn.h>
+#include <openssl/crypto.h> // for OPENSSL_free
+
+#include "s2/base/integral_types.h"
+#include "s2/base/logging.h"
+#include "s2/third_party/absl/base/macros.h"
+#include "s2/third_party/absl/container/fixed_array.h"
+
+using std::max;
+using std::min;
+
+// Define storage for constants.
+const int ExactFloat::kMinExp;
+const int ExactFloat::kMaxExp;
+const int ExactFloat::kMaxPrec;
+const int32 ExactFloat::kExpNaN;
+const int32 ExactFloat::kExpInfinity;
+const int32 ExactFloat::kExpZero;
+const int ExactFloat::kDoubleMantissaBits;
+
+// To simplify the overflow/underflow logic, we limit the exponent and
+// precision range so that (2 * bn_exp_) does not overflow an "int". We take
+// advantage of this, for example, by only checking for overflow/underflow
+// *after* multiplying two numbers.
+static_assert(
+ ExactFloat::kMaxExp <= INT_MAX / 2 &&
+ ExactFloat::kMinExp - ExactFloat::kMaxPrec >= INT_MIN / 2,
+ "exactfloat exponent might overflow");
+
+// We define a few simple extensions to the OpenSSL's BIGNUM interface.
+// In some cases these depend on BIGNUM internal fields, so they might
+// require tweaking if the BIGNUM implementation changes significantly.
+// These are just thin wrappers for BoringSSL.
+
+#ifdef OPENSSL_IS_BORINGSSL
+
+inline static void BN_ext_set_uint64(BIGNUM* bn, uint64 v) {
+ S2_CHECK(BN_set_u64(bn, v));
+}
+
+// Return the absolute value of a BIGNUM as a 64-bit unsigned integer.
+// Requires that BIGNUM fits into 64 bits.
+inline static uint64 BN_ext_get_uint64(const BIGNUM* bn) {
+ uint64_t u64;
+ if (!BN_get_u64(bn, &u64)) {
+ S2_DCHECK(false) << "BN has " << BN_num_bits(bn) << " bits";
+ return 0;
+ }
+ return u64;
+}
+
+static int BN_ext_count_low_zero_bits(const BIGNUM* bn) {
+ return BN_count_low_zero_bits(bn);
+}
+
+#else // !defined(OPENSSL_IS_BORINGSSL)
+
+// Set a BIGNUM to the given unsigned 64-bit value.
+inline static void BN_ext_set_uint64(BIGNUM* bn, uint64 v) {
+#if BN_BITS2 == 64
+ S2_CHECK(BN_set_word(bn, v));
+#else
+ static_assert(BN_BITS2 == 32, "at least 32 bit openssl build needed");
+ S2_CHECK(BN_set_word(bn, static_cast<uint32>(v >> 32)));
+ S2_CHECK(BN_lshift(bn, bn, 32));
+ S2_CHECK(BN_add_word(bn, static_cast<uint32>(v)));
+#endif
+}
+
+// Return the absolute value of a BIGNUM as a 64-bit unsigned integer.
+// Requires that BIGNUM fits into 64 bits.
+inline static uint64 BN_ext_get_uint64(const BIGNUM* bn) {
+ S2_DCHECK_LE(BN_num_bytes(bn), sizeof(uint64));
+#if BN_BITS2 == 64
+ return BN_get_word(bn);
+#else
+ static_assert(BN_BITS2 == 32, "at least 32 bit openssl build needed");
+ if (bn->top == 0) return 0;
+ if (bn->top == 1) return BN_get_word(bn);
+ S2_DCHECK_EQ(bn->top, 2);
+ return (static_cast<uint64>(bn->d[1]) << 32) + bn->d[0];
+#endif
+}
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+
+// Count the number of low-order zero bits in the given BIGNUM (ignoring its
+// sign). Returns 0 if the argument is zero.
+static int BN_ext_count_low_zero_bits(const BIGNUM* bn) {
+ int count = 0;
+ for (int i = 0; i < bn->top; ++i) {
+ BN_ULONG w = bn->d[i];
+ if (w == 0) {
+ count += 8 * sizeof(BN_ULONG);
+ } else {
+ for (; (w & 1) == 0; w >>= 1) {
+ ++count;
+ }
+ break;
+ }
+ }
+ return count;
+}
+
+#else // OPENSSL_VERSION_NUMBER >= 0x10100000L
+
+static int BN_ext_count_low_zero_bits(const BIGNUM* bn) {
+ // In OpenSSL >= 1.1, BIGNUM is an opaque type, so d and top
+ // cannot be accessed. The bytes must be copied out at a ~25%
+ // performance penalty.
+ absl::FixedArray<unsigned char> bytes(BN_num_bytes(bn));
+ // "le" indicates little endian.
+ S2_CHECK_EQ(BN_bn2lebinpad(bn, bytes.data(), bytes.size()), bytes.size());
+
+ int count = 0;
+ for (unsigned char c : bytes) {
+ if (c == 0) {
+ count += 8;
+ } else {
+ for (; (c & 1) == 0; c >>= 1) {
+ ++count;
+ }
+ break;
+ }
+ }
+ return count;
+}
+
+#endif // OPENSSL_VERSION_NUMBER >= 0x10100000L
+
+#endif // !defined(OPENSSL_IS_BORINGSSL)
+
+ExactFloat::ExactFloat(double v) {
+ sign_ = std::signbit(v) ? -1 : 1;
+ if (std::isnan(v)) {
+ set_nan();
+ } else if (std::isinf(v)) {
+ set_inf(sign_);
+ } else {
+ // The following code is much simpler than messing about with bit masks,
+ // has the advantage of handling denormalized numbers and zero correctly,
+ // and is actually quite efficient (at least compared to the rest of this
+ // code). "f" is a fraction in the range [0.5, 1), so if we shift it left
+ // by the number of mantissa bits in a double (53, including the leading
+ // "1") then the result is always an integer.
+ int exp;
+ double f = frexp(fabs(v), &exp);
+ uint64 m = static_cast<uint64>(ldexp(f, kDoubleMantissaBits));
+ BN_ext_set_uint64(bn_.get(), m);
+ bn_exp_ = exp - kDoubleMantissaBits;
+ Canonicalize();
+ }
+}
+
+ExactFloat::ExactFloat(int v) {
+ sign_ = (v >= 0) ? 1 : -1;
+ // Note that this works even for INT_MIN because the parameter type for
+ // BN_set_word() is unsigned.
+ S2_CHECK(BN_set_word(bn_.get(), abs(v)));
+ bn_exp_ = 0;
+ Canonicalize();
+}
+
+ExactFloat::ExactFloat(const ExactFloat& b)
+ : sign_(b.sign_),
+ bn_exp_(b.bn_exp_) {
+ BN_copy(bn_.get(), b.bn_.get());
+}
+
+ExactFloat ExactFloat::SignedZero(int sign) {
+ ExactFloat r;
+ r.set_zero(sign);
+ return r;
+}
+
+ExactFloat ExactFloat::Infinity(int sign) {
+ ExactFloat r;
+ r.set_inf(sign);
+ return r;
+}
+
+ExactFloat ExactFloat::NaN() {
+ ExactFloat r;
+ r.set_nan();
+ return r;
+}
+
+int ExactFloat::prec() const {
+ return BN_num_bits(bn_.get());
+}
+
+int ExactFloat::exp() const {
+ S2_DCHECK(is_normal());
+ return bn_exp_ + BN_num_bits(bn_.get());
+}
+
+void ExactFloat::set_zero(int sign) {
+ sign_ = sign;
+ bn_exp_ = kExpZero;
+ if (!BN_is_zero(bn_.get())) BN_zero(bn_.get());
+}
+
+void ExactFloat::set_inf(int sign) {
+ sign_ = sign;
+ bn_exp_ = kExpInfinity;
+ if (!BN_is_zero(bn_.get())) BN_zero(bn_.get());
+}
+
+void ExactFloat::set_nan() {
+ sign_ = 1;
+ bn_exp_ = kExpNaN;
+ if (!BN_is_zero(bn_.get())) BN_zero(bn_.get());
+}
+
+double ExactFloat::ToDouble() const {
+ // If the mantissa has too many bits, we need to round it.
+ if (prec() <= kDoubleMantissaBits) {
+ return ToDoubleHelper();
+ } else {
+ ExactFloat r = RoundToMaxPrec(kDoubleMantissaBits, kRoundTiesToEven);
+ return r.ToDoubleHelper();
+ }
+}
+
+double ExactFloat::ToDoubleHelper() const {
+ S2_DCHECK_LE(BN_num_bits(bn_.get()), kDoubleMantissaBits);
+ if (!is_normal()) {
+ if (is_zero()) return copysign(0, sign_);
+ if (is_inf()) {
+ return std::copysign(std::numeric_limits<double>::infinity(), sign_);
+ }
+ return std::copysign(std::numeric_limits<double>::quiet_NaN(), sign_);
+ }
+ uint64 d_mantissa = BN_ext_get_uint64(bn_.get());
+ // We rely on ldexp() to handle overflow and underflow. (It will return a
+ // signed zero or infinity if the result is too small or too large.)
+ return sign_ * ldexp(static_cast<double>(d_mantissa), bn_exp_);
+}
+
+ExactFloat ExactFloat::RoundToMaxPrec(int max_prec, RoundingMode mode) const {
+ // The "kRoundTiesToEven" mode requires at least 2 bits of precision
+ // (otherwise both adjacent representable values may be odd).
+ S2_DCHECK_GE(max_prec, 2);
+ S2_DCHECK_LE(max_prec, kMaxPrec);
+
+ // The following test also catches zero, infinity, and NaN.
+ int shift = prec() - max_prec;
+ if (shift <= 0) return *this;
+
+ // Round by removing the appropriate number of bits from the mantissa. Note
+ // that if the value is rounded up to a power of 2, the high-order bit
+ // position may increase, but in that case Canonicalize() will remove at
+ // least one zero bit and so the output will still have prec() <= max_prec.
+ return RoundToPowerOf2(bn_exp_ + shift, mode);
+}
+
+ExactFloat ExactFloat::RoundToPowerOf2(int bit_exp, RoundingMode mode) const {
+ S2_DCHECK_GE(bit_exp, kMinExp - kMaxPrec);
+ S2_DCHECK_LE(bit_exp, kMaxExp);
+
+ // If the exponent is already large enough, or the value is zero, infinity,
+ // or NaN, then there is nothing to do.
+ int shift = bit_exp - bn_exp_;
+ if (shift <= 0) return *this;
+ S2_DCHECK(is_normal());
+
+ // Convert rounding up/down to toward/away from zero, so that we don't need
+ // to consider the sign of the number from this point onward.
+ if (mode == kRoundTowardPositive) {
+ mode = (sign_ > 0) ? kRoundAwayFromZero : kRoundTowardZero;
+ } else if (mode == kRoundTowardNegative) {
+ mode = (sign_ > 0) ? kRoundTowardZero : kRoundAwayFromZero;
+ }
+
+ // Rounding consists of right-shifting the mantissa by "shift", and then
+ // possibly incrementing the result (depending on the rounding mode, the
+ // bits that were discarded, and sometimes the lowest kept bit). The
+ // following code figures out whether we need to increment.
+ ExactFloat r;
+ bool increment = false;
+ if (mode == kRoundTowardZero) {
+ // Never increment.
+ } else if (mode == kRoundTiesAwayFromZero) {
+ // Increment if the highest discarded bit is 1.
+ if (BN_is_bit_set(bn_.get(), shift - 1))
+ increment = true;
+ } else if (mode == kRoundAwayFromZero) {
+ // Increment unless all discarded bits are zero.
+ if (BN_ext_count_low_zero_bits(bn_.get()) < shift)
+ increment = true;
+ } else {
+ S2_DCHECK_EQ(mode, kRoundTiesToEven);
+ // Let "w/xyz" denote a mantissa where "w" is the lowest kept bit and
+ // "xyz" are the discarded bits. Then using regexp notation:
+ // ./0.* -> Don't increment (fraction < 1/2)
+ // 0/10* -> Don't increment (fraction = 1/2, kept part even)
+ // 1/10* -> Increment (fraction = 1/2, kept part odd)
+ // ./1.*1.* -> Increment (fraction > 1/2)
+ if (BN_is_bit_set(bn_.get(), shift - 1) &&
+ ((BN_is_bit_set(bn_.get(), shift) ||
+ BN_ext_count_low_zero_bits(bn_.get()) < shift - 1))) {
+ increment = true;
+ }
+ }
+ r.bn_exp_ = bn_exp_ + shift;
+ S2_CHECK(BN_rshift(r.bn_.get(), bn_.get(), shift));
+ if (increment) {
+ S2_CHECK(BN_add_word(r.bn_.get(), 1));
+ }
+ r.sign_ = sign_;
+ r.Canonicalize();
+ return r;
+}
+
+int ExactFloat::NumSignificantDigitsForPrec(int prec) {
+ // The simplest bound is
+ //
+ // d <= 1 + ceil(prec * log10(2))
+ //
+ // The following bound is tighter by 0.5 digits on average, but requires
+ // the exponent to be known as well:
+ //
+ // d <= ceil(exp * log10(2)) - floor((exp - prec) * log10(2))
+ //
+ // Since either of these bounds can be too large by 0, 1, or 2 digits, we
+ // stick with the simpler first bound.
+ return static_cast<int>(1 + ceil(prec * (M_LN2 / M_LN10)));
+}
+
+// Numbers are always formatted with at least this many significant digits.
+// This prevents small integers from being formatted in exponential notation
+// (e.g. 1024 formatted as 1e+03), and also avoids the confusion of having
+// supposedly "high precision" numbers formatted with just 1 or 2 digits
+// (e.g. 1/512 == 0.001953125 formatted as 0.002).
+static const int kMinSignificantDigits = 10;
+
+string ExactFloat::ToString() const {
+ int max_digits = max(kMinSignificantDigits,
+ NumSignificantDigitsForPrec(prec()));
+ return ToStringWithMaxDigits(max_digits);
+}
+
+string ExactFloat::ToStringWithMaxDigits(int max_digits) const {
+ S2_DCHECK_GT(max_digits, 0);
+ if (!is_normal()) {
+ if (is_nan()) return "nan";
+ if (is_zero()) return (sign_ < 0) ? "-0" : "0";
+ return (sign_ < 0) ? "-inf" : "inf";
+ }
+ string digits;
+ int exp10 = GetDecimalDigits(max_digits, &digits);
+ string str;
+ if (sign_ < 0) str.push_back('-');
+
+ // We use the standard '%g' formatting rules. If the exponent is less than
+ // -4 or greater than or equal to the requested precision (i.e., max_digits)
+ // then we use exponential notation.
+ //
+ // But since "exp10" is the base-10 exponent corresponding to a mantissa in
+ // the range [0.1, 1), whereas the '%g' rules assume a mantissa in the range
+ // [1.0, 10), we need to adjust these parameters by 1.
+ if (exp10 <= -4 || exp10 > max_digits) {
+ // Use exponential format.
+ str.push_back(digits[0]);
+ if (digits.size() > 1) {
+ str.push_back('.');
+ str.append(digits.begin() + 1, digits.end());
+ }
+ char exp_buf[20];
+ sprintf(exp_buf, "e%+02d", exp10 - 1);
+ str += exp_buf;
+ } else {
+ // Use fixed format. We split this into two cases depending on whether
+ // the integer portion is non-zero or not.
+ if (exp10 > 0) {
+ if (exp10 >= digits.size()) {
+ str += digits;
+ for (int i = exp10 - digits.size(); i > 0; --i) {
+ str.push_back('0');
+ }
+ } else {
+ str.append(digits.begin(), digits.begin() + exp10);
+ str.push_back('.');
+ str.append(digits.begin() + exp10, digits.end());
+ }
+ } else {
+ str += "0.";
+ for (int i = exp10; i < 0; ++i) {
+ str.push_back('0');
+ }
+ str += digits;
+ }
+ }
+ return str;
+}
+
+// Increment an unsigned integer represented as a string of ASCII digits.
+static void IncrementDecimalDigits(string* digits) {
+ string::iterator pos = digits->end();
+ while (--pos >= digits->begin()) {
+ if (*pos < '9') { ++*pos; return; }
+ *pos = '0';
+ }
+ digits->insert(0, "1");
+}
+
+int ExactFloat::GetDecimalDigits(int max_digits, string* digits) const {
+ S2_DCHECK(is_normal());
+ // Convert the value to the form (bn * (10 ** bn_exp10)) where "bn" is a
+ // positive integer (BIGNUM).
+ BIGNUM* bn = BN_new();
+ int bn_exp10;
+ if (bn_exp_ >= 0) {
+ // The easy case: bn = bn_ * (2 ** bn_exp_)), bn_exp10 = 0.
+ S2_CHECK(BN_lshift(bn, bn_.get(), bn_exp_));
+ bn_exp10 = 0;
+ } else {
+ // Set bn = bn_ * (5 ** -bn_exp_) and bn_exp10 = bn_exp_. This is
+ // equivalent to the original value of (bn_ * (2 ** bn_exp_)).
+ BIGNUM* power = BN_new();
+ S2_CHECK(BN_set_word(power, -bn_exp_));
+ S2_CHECK(BN_set_word(bn, 5));
+ BN_CTX* ctx = BN_CTX_new();
+ S2_CHECK(BN_exp(bn, bn, power, ctx));
+ S2_CHECK(BN_mul(bn, bn, bn_.get(), ctx));
+ BN_CTX_free(ctx);
+ BN_free(power);
+ bn_exp10 = bn_exp_;
+ }
+ // Now convert "bn" to a decimal string.
+ char* all_digits = BN_bn2dec(bn);
+ S2_DCHECK(all_digits != nullptr);
+ BN_free(bn);
+ // Check whether we have too many digits and round if necessary.
+ int num_digits = strlen(all_digits);
+ if (num_digits <= max_digits) {
+ *digits = all_digits;
+ } else {
+ digits->assign(all_digits, max_digits);
+ // Standard "printf" formatting rounds ties to an even number. This means
+ // that we round up (away from zero) if highest discarded digit is '5' or
+ // more, unless all other discarded digits are zero in which case we round
+ // up only if the lowest kept digit is odd.
+ if (all_digits[max_digits] >= '5' &&
+ ((all_digits[max_digits-1] & 1) == 1 ||
+ strpbrk(all_digits + max_digits + 1, "123456789") != nullptr)) {
+ // This can increase the number of digits by 1, but in that case at
+ // least one trailing zero will be stripped off below.
+ IncrementDecimalDigits(digits);
+ }
+ // Adjust the base-10 exponent to reflect the digits we have removed.
+ bn_exp10 += num_digits - max_digits;
+ }
+ OPENSSL_free(all_digits);
+
+ // Now strip any trailing zeros.
+ S2_DCHECK_NE((*digits)[0], '0');
+ string::iterator pos = digits->end();
+ while (pos[-1] == '0') --pos;
+ if (pos < digits->end()) {
+ bn_exp10 += digits->end() - pos;
+ digits->erase(pos, digits->end());
+ }
+ S2_DCHECK_LE(digits->size(), max_digits);
+
+ // Finally, we adjust the base-10 exponent so that the mantissa is a
+ // fraction in the range [0.1, 1) rather than an integer.
+ return bn_exp10 + digits->size();
+}
+
+string ExactFloat::ToUniqueString() const {
+ char prec_buf[20];
+ sprintf(prec_buf, "<%d>", prec());
+ return ToString() + prec_buf;
+}
+
+ExactFloat& ExactFloat::operator=(const ExactFloat& b) {
+ if (this != &b) {
+ sign_ = b.sign_;
+ bn_exp_ = b.bn_exp_;
+ BN_copy(bn_.get(), b.bn_.get());
+ }
+ return *this;
+}
+
+ExactFloat ExactFloat::operator-() const {
+ return CopyWithSign(-sign_);
+}
+
+ExactFloat operator+(const ExactFloat& a, const ExactFloat& b) {
+ return ExactFloat::SignedSum(a.sign_, &a, b.sign_, &b);
+}
+
+ExactFloat operator-(const ExactFloat& a, const ExactFloat& b) {
+ return ExactFloat::SignedSum(a.sign_, &a, -b.sign_, &b);
+}
+
+ExactFloat ExactFloat::SignedSum(int a_sign, const ExactFloat* a,
+ int b_sign, const ExactFloat* b) {
+ if (!a->is_normal() || !b->is_normal()) {
+ // Handle zero, infinity, and NaN according to IEEE 754-2008.
+ if (a->is_nan()) return *a;
+ if (b->is_nan()) return *b;
+ if (a->is_inf()) {
+ // Adding two infinities with opposite sign yields NaN.
+ if (b->is_inf() && a_sign != b_sign) return NaN();
+ return Infinity(a_sign);
+ }
+ if (b->is_inf()) return Infinity(b_sign);
+ if (a->is_zero()) {
+ if (!b->is_zero()) return b->CopyWithSign(b_sign);
+ // Adding two zeros with the same sign preserves the sign.
+ if (a_sign == b_sign) return SignedZero(a_sign);
+ // Adding two zeros of opposite sign produces +0.
+ return SignedZero(+1);
+ }
+ S2_DCHECK(b->is_zero());
+ return a->CopyWithSign(a_sign);
+ }
+ // Swap the numbers if necessary so that "a" has the larger bn_exp_.
+ if (a->bn_exp_ < b->bn_exp_) {
+ using std::swap;
+ swap(a_sign, b_sign);
+ swap(a, b);
+ }
+ // Shift "a" if necessary so that both values have the same bn_exp_.
+ ExactFloat r;
+ if (a->bn_exp_ > b->bn_exp_) {
+ S2_CHECK(BN_lshift(r.bn_.get(), a->bn_.get(), a->bn_exp_ - b->bn_exp_));
+ a = &r; // The only field of "a" used below is bn_.
+ }
+ r.bn_exp_ = b->bn_exp_;
+ if (a_sign == b_sign) {
+ S2_CHECK(BN_add(r.bn_.get(), a->bn_.get(), b->bn_.get()));
+ r.sign_ = a_sign;
+ } else {
+ // Note that the BIGNUM documentation is out of date -- all methods now
+ // allow the result to be the same as any input argument, so it is okay if
+ // (a == &r) due to the shift above.
+ S2_CHECK(BN_sub(r.bn_.get(), a->bn_.get(), b->bn_.get()));
+ if (BN_is_zero(r.bn_.get())) {
+ r.sign_ = +1;
+ } else if (BN_is_negative(r.bn_.get())) {
+ // The magnitude of "b" was larger.
+ r.sign_ = b_sign;
+ BN_set_negative(r.bn_.get(), false);
+ } else {
+ // They were equal, or the magnitude of "a" was larger.
+ r.sign_ = a_sign;
+ }
+ }
+ r.Canonicalize();
+ return r;
+}
+
+void ExactFloat::Canonicalize() {
+ if (!is_normal()) return;
+
+ // Underflow/overflow occurs if exp() is not in [kMinExp, kMaxExp].
+ // We also convert a zero mantissa to signed zero.
+ int my_exp = exp();
+ if (my_exp < kMinExp || BN_is_zero(bn_.get())) {
+ set_zero(sign_);
+ } else if (my_exp > kMaxExp) {
+ set_inf(sign_);
+ } else if (!BN_is_odd(bn_.get())) {
+ // Remove any low-order zero bits from the mantissa.
+ S2_DCHECK(!BN_is_zero(bn_.get()));
+ int shift = BN_ext_count_low_zero_bits(bn_.get());
+ if (shift > 0) {
+ S2_CHECK(BN_rshift(bn_.get(), bn_.get(), shift));
+ bn_exp_ += shift;
+ }
+ }
+ // If the mantissa has too many bits, we replace it by NaN to indicate
+ // that an inexact calculation has occurred.
+ if (prec() > kMaxPrec) {
+ set_nan();
+ }
+}
+
+ExactFloat operator*(const ExactFloat& a, const ExactFloat& b) {
+ int result_sign = a.sign_ * b.sign_;
+ if (!a.is_normal() || !b.is_normal()) {
+ // Handle zero, infinity, and NaN according to IEEE 754-2008.
+ if (a.is_nan()) return a;
+ if (b.is_nan()) return b;
+ if (a.is_inf()) {
+ // Infinity times zero yields NaN.
+ if (b.is_zero()) return ExactFloat::NaN();
+ return ExactFloat::Infinity(result_sign);
+ }
+ if (b.is_inf()) {
+ if (a.is_zero()) return ExactFloat::NaN();
+ return ExactFloat::Infinity(result_sign);
+ }
+ S2_DCHECK(a.is_zero() || b.is_zero());
+ return ExactFloat::SignedZero(result_sign);
+ }
+ ExactFloat r;
+ r.sign_ = result_sign;
+ r.bn_exp_ = a.bn_exp_ + b.bn_exp_;
+ BN_CTX* ctx = BN_CTX_new();
+ S2_CHECK(BN_mul(r.bn_.get(), a.bn_.get(), b.bn_.get(), ctx));
+ BN_CTX_free(ctx);
+ r.Canonicalize();
+ return r;
+}
+
+bool operator==(const ExactFloat& a, const ExactFloat& b) {
+ // NaN is not equal to anything, not even itself.
+ if (a.is_nan() || b.is_nan()) return false;
+
+ // Since Canonicalize() strips low-order zero bits, all other cases
+ // (including non-normal values) require bn_exp_ to be equal.
+ if (a.bn_exp_ != b.bn_exp_) return false;
+
+ // Positive and negative zero are equal.
+ if (a.is_zero() && b.is_zero()) return true;
+
+ // Otherwise, the signs and mantissas must match. Note that non-normal
+ // values such as infinity have a mantissa of zero.
+ return a.sign_ == b.sign_ && BN_ucmp(a.bn_.get(), b.bn_.get()) == 0;
+}
+
+int ExactFloat::ScaleAndCompare(const ExactFloat& b) const {
+ S2_DCHECK(is_normal() && b.is_normal() && bn_exp_ >= b.bn_exp_);
+ ExactFloat tmp = *this;
+ S2_CHECK(BN_lshift(tmp.bn_.get(), tmp.bn_.get(), bn_exp_ - b.bn_exp_));
+ return BN_ucmp(tmp.bn_.get(), b.bn_.get());
+}
+
+bool ExactFloat::UnsignedLess(const ExactFloat& b) const {
+ // Handle the zero/infinity cases (NaN has already been done).
+ if (is_inf() || b.is_zero()) return false;
+ if (is_zero() || b.is_inf()) return true;
+ // If the high-order bit positions differ, we are done.
+ int cmp = exp() - b.exp();
+ if (cmp != 0) return cmp < 0;
+ // Otherwise shift one of the two values so that they both have the same
+ // bn_exp_ and then compare the mantissas.
+ return (bn_exp_ >= b.bn_exp_ ?
+ ScaleAndCompare(b) < 0 : b.ScaleAndCompare(*this) > 0);
+}
+
+bool operator<(const ExactFloat& a, const ExactFloat& b) {
+ // NaN is unordered compared to everything, including itself.
+ if (a.is_nan() || b.is_nan()) return false;
+ // Positive and negative zero are equal.
+ if (a.is_zero() && b.is_zero()) return false;
+ // Otherwise, anything negative is less than anything positive.
+ if (a.sign_ != b.sign_) return a.sign_ < b.sign_;
+ // Now we just compare absolute values.
+ return (a.sign_ > 0) ? a.UnsignedLess(b) : b.UnsignedLess(a);
+}
+
+ExactFloat fabs(const ExactFloat& a) {
+ return abs(a);
+}
+
+ExactFloat abs(const ExactFloat& a) {
+ return a.CopyWithSign(+1);
+}
+
+ExactFloat fmax(const ExactFloat& a, const ExactFloat& b) {
+ // If one argument is NaN, return the other argument.
+ if (a.is_nan()) return b;
+ if (b.is_nan()) return a;
+ // Not required by IEEE 754, but we prefer +0 over -0.
+ if (a.sign_ != b.sign_) {
+ return (a.sign_ < b.sign_) ? b : a;
+ }
+ return (a < b) ? b : a;
+}
+
+ExactFloat fmin(const ExactFloat& a, const ExactFloat& b) {
+ // If one argument is NaN, return the other argument.
+ if (a.is_nan()) return b;
+ if (b.is_nan()) return a;
+ // Not required by IEEE 754, but we prefer -0 over +0.
+ if (a.sign_ != b.sign_) {
+ return (a.sign_ < b.sign_) ? a : b;
+ }
+ return (a < b) ? a : b;
+}
+
+ExactFloat fdim(const ExactFloat& a, const ExactFloat& b) {
+ // This formulation has the correct behavior for NaNs.
+ return (a <= b) ? 0 : (a - b);
+}
+
+ExactFloat ceil(const ExactFloat& a) {
+ return a.RoundToPowerOf2(0, ExactFloat::kRoundTowardPositive);
+}
+
+ExactFloat floor(const ExactFloat& a) {
+ return a.RoundToPowerOf2(0, ExactFloat::kRoundTowardNegative);
+}
+
+ExactFloat trunc(const ExactFloat& a) {
+ return a.RoundToPowerOf2(0, ExactFloat::kRoundTowardZero);
+}
+
+ExactFloat round(const ExactFloat& a) {
+ return a.RoundToPowerOf2(0, ExactFloat::kRoundTiesAwayFromZero);
+}
+
+ExactFloat rint(const ExactFloat& a) {
+ return a.RoundToPowerOf2(0, ExactFloat::kRoundTiesToEven);
+}
+
+template <class T>
+T ExactFloat::ToInteger(RoundingMode mode) const {
+ using std::numeric_limits;
+ static_assert(sizeof(T) <= sizeof(uint64), "max 64 bits supported");
+ static_assert(numeric_limits<T>::is_signed, "only signed types supported");
+ const int64 kMinValue = numeric_limits<T>::min();
+ const int64 kMaxValue = numeric_limits<T>::max();
+
+ ExactFloat r = RoundToPowerOf2(0, mode);
+ if (r.is_nan()) return kMaxValue;
+ if (r.is_zero()) return 0;
+ if (!r.is_inf()) {
+ // If the unsigned value has more than 63 bits it is always clamped.
+ if (r.exp() < 64) {
+ int64 value = BN_ext_get_uint64(r.bn_.get()) << r.bn_exp_;
+ if (r.sign_ < 0) value = -value;
+ return max(kMinValue, min(kMaxValue, value));
+ }
+ }
+ return (r.sign_ < 0) ? kMinValue : kMaxValue;
+}
+
+long lrint(const ExactFloat& a) {
+ return a.ToInteger<long>(ExactFloat::kRoundTiesToEven);
+}
+
+long long llrint(const ExactFloat& a) {
+ return a.ToInteger<long long>(ExactFloat::kRoundTiesToEven);
+}
+
+long lround(const ExactFloat& a) {
+ return a.ToInteger<long>(ExactFloat::kRoundTiesAwayFromZero);
+}
+
+long long llround(const ExactFloat& a) {
+ return a.ToInteger<long long>(ExactFloat::kRoundTiesAwayFromZero);
+}
+
+ExactFloat copysign(const ExactFloat& a, const ExactFloat& b) {
+ return a.CopyWithSign(b.sign_);
+}
+
+ExactFloat frexp(const ExactFloat& a, int* exp) {
+ if (!a.is_normal()) {
+ // If a == 0, exp should be zero. If a.is_inf() or a.is_nan(), exp is not
+ // defined but the glibc implementation returns zero.
+ *exp = 0;
+ return a;
+ }
+ *exp = a.exp();
+ return ldexp(a, -a.exp());
+}
+
+ExactFloat ldexp(const ExactFloat& a, int exp) {
+ if (!a.is_normal()) return a;
+
+ // To prevent integer overflow, we first clamp "exp" so that
+ // (kMinExp - 1) <= (a_exp + exp) <= (kMaxExp + 1).
+ int a_exp = a.exp();
+ exp = min(ExactFloat::kMaxExp + 1 - a_exp,
+ max(ExactFloat::kMinExp - 1 + a_exp, exp));
+
+ // Now modify the exponent and check for overflow/underflow.
+ ExactFloat r = a;
+ r.bn_exp_ += exp;
+ r.Canonicalize();
+ return r;
+}
+
+ExactFloat scalbln(const ExactFloat& a, long exp) {
+ // Clamp the exponent to the range of "int" in order to avoid truncation.
+ exp = max(static_cast<long>(INT_MIN), min(static_cast<long>(INT_MAX), exp));
+ return ldexp(a, exp);
+}
+
+int ilogb(const ExactFloat& a) {
+ if (a.is_zero()) return FP_ILOGB0;
+ if (a.is_inf()) return INT_MAX;
+ if (a.is_nan()) return FP_ILOGBNAN;
+ // a.exp() assumes the significand is in the range [0.5, 1).
+ return a.exp() - 1;
+}
+
+ExactFloat logb(const ExactFloat& a) {
+ if (a.is_zero()) return ExactFloat::Infinity(-1);
+ if (a.is_inf()) return ExactFloat::Infinity(+1); // Even if a < 0.
+ if (a.is_nan()) return a;
+ // exp() assumes the significand is in the range [0.5,1).
+ return ExactFloat(a.exp() - 1);
+}
+
+ExactFloat ExactFloat::Unimplemented() {
+ S2_LOG(FATAL) << "Unimplemented ExactFloat method called";
+ return NaN();
+}
--- /dev/null
+// Copyright 2008 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+
+#include "s2/util/math/mathutil.h"
+
+#include <cmath>
+#include <cstdlib>
+
+namespace {
+// Returns the sign of x:
+// -1 if x < 0,
+// +1 if x > 0,
+// 0 if x = 0.
+template <class T>
+inline T sgn(const T x) {
+ return (x == 0 ? 0 : (x < 0 ? -1 : 1));
+}
+} // namespace
+
+bool MathUtil::RealRootsForCubic(long double const a,
+ long double const b,
+ long double const c,
+ long double *const r1,
+ long double *const r2,
+ long double *const r3) {
+ // According to Numerical Recipes (pp. 184-5), what
+ // follows is an arrangement of computations to
+ // compute the roots of a cubic that minimizes
+ // roundoff error (as pointed out by A.J. Glassman).
+
+ long double const a_squared = a * a, a_third = a / 3.0, b_tripled = 3.0 * b;
+ long double const Q = (a_squared - b_tripled) / 9.0;
+ long double const R =
+ (2.0 * a_squared * a - 3.0 * a * b_tripled + 27.0 * c) / 54.0;
+
+ long double const R_squared = R * R;
+ long double const Q_cubed = Q * Q * Q;
+
+ if (R_squared < Q_cubed) {
+ long double const root_Q = sqrt(Q);
+ long double const two_pi_third = 2.0 * M_PI / 3.0;
+ long double const theta_third = acos(R / sqrt(Q_cubed)) / 3.0;
+ long double const minus_two_root_Q = -2.0 * root_Q;
+
+ *r1 = minus_two_root_Q * cos(theta_third) - a_third;
+ *r2 = minus_two_root_Q * cos(theta_third + two_pi_third) - a_third;
+ *r3 = minus_two_root_Q * cos(theta_third - two_pi_third) - a_third;
+
+ return true;
+ }
+
+ long double const A =
+ -sgn(R) * pow(std::abs(R) + sqrt(R_squared - Q_cubed), 1.0 / 3.0L);
+
+ if (A != 0.0) { // in which case, B from NR is zero
+ *r1 = A + Q / A - a_third;
+ return false;
+ }
+
+ *r1 = *r2 = *r3 = -a_third;
+ return true;
+}
--- /dev/null
+// Copyright 2004 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+
+// Provide definitions for length unit constants
+
+#include "s2/util/units/length-units.h"
+
+const char * const util::units::LengthBase::output_suffix = "m";
--- /dev/null
+#include "openssl/opensslv.h"
+
+#define XSTR(x) STR(x)
+#define STR(x) #x
+#ifdef SHLIB_VERSION_NUMBER
+echo XSTR(SHLIB_VERSION_NUMBER)
+#else
+echo XSTR(OPENSSL_SHLIB_VERSION)
+#endif
--- /dev/null
+
+#ifndef WK_GEOGRAPHY_H
+#define WK_GEOGRAPHY_H
+
+#include "wk/rcpp-io.hpp"
+#include "wk/reader.hpp"
+#include "wk/geometry-handler.hpp"
+
+#include "point-geography.h"
+#include "polyline-geography.h"
+#include "polygon-geography.h"
+#include "geography-collection.h"
+
+#include <Rcpp.h>
+#include "geography.h"
+
+
+class WKGeographyWriter: public WKGeometryHandler {
+public:
+ Rcpp::List output;
+ R_xlen_t featureId;
+
+ WKGeographyWriter(R_xlen_t size):
+ output(size),
+ builder(nullptr),
+ oriented(false),
+ check(true) {}
+
+ void setOriented(bool oriented) {
+ this->oriented = oriented;
+ }
+
+ void setCheck(bool check) {
+ this->check = check;
+ }
+
+ void nextFeatureStart(size_t featureId) {
+ this->builder = std::unique_ptr<GeographyBuilder>(nullptr);
+ this->featureId = featureId;
+ }
+
+ void nextNull(size_t featureId) {
+ this->output[featureId] = R_NilValue;
+ }
+
+ void nextGeometryStart(const WKGeometryMeta& meta, uint32_t partId) {
+ if (!this->builder) {
+ switch (meta.geometryType) {
+ case WKGeometryType::Point:
+ case WKGeometryType::MultiPoint:
+ this->builder = absl::make_unique<PointGeography::Builder>();
+ break;
+ case WKGeometryType::LineString:
+ case WKGeometryType::MultiLineString:
+ this->builder = absl::make_unique<PolylineGeography::Builder>();
+ break;
+ case WKGeometryType::Polygon:
+ case WKGeometryType::MultiPolygon:
+ this->builder = absl::make_unique<PolygonGeography::Builder>(
+ this->oriented,
+ this->check
+ );
+ break;
+ case WKGeometryType::GeometryCollection:
+ this->builder = absl::make_unique<GeographyCollection::Builder>(
+ this->oriented,
+ this->check
+ );
+ break;
+ default:
+ std::stringstream err;
+ err << "Unknown geometry type in geography builder: " << meta.geometryType;
+ Rcpp::stop(err.str());
+ }
+ }
+
+ this->builder->nextGeometryStart(meta, partId);
+ }
+
+ void nextLinearRingStart(const WKGeometryMeta& meta, uint32_t size, uint32_t ringId) {
+ this->builder->nextLinearRingStart(meta, size, ringId);
+ }
+
+ void nextCoordinate(const WKGeometryMeta& meta, const WKCoord& coord, uint32_t coordId) {
+ this->builder->nextCoordinate(meta, coord, coordId);
+ }
+
+ void nextLinearRingEnd(const WKGeometryMeta& meta, uint32_t size, uint32_t ringId) {
+ this->builder->nextLinearRingEnd(meta, size, ringId);
+ }
+
+ void nextGeometryEnd(const WKGeometryMeta& meta, uint32_t partId) {
+ this->builder->nextGeometryEnd(meta, partId);
+ }
+
+ void nextFeatureEnd(size_t featureId) {
+ if (this->builder) {
+ std::unique_ptr<Geography> feature = builder->build();
+ this->output[featureId] = Rcpp::XPtr<Geography>(feature.release());
+ }
+ }
+
+private:
+ std::unique_ptr<GeographyBuilder> builder;
+ bool oriented;
+ bool check;
+};
+
+
+class WKGeographyReader: public WKReader {
+public:
+
+ WKGeographyReader(WKRcppSEXPProvider& provider):
+ WKReader(provider), provider(provider) {}
+
+ void readFeature(size_t featureId) {
+ this->handler->nextFeatureStart(featureId);
+
+ if (this->provider.featureIsNull()) {
+ this->handler->nextNull(featureId);
+ } else {
+ Rcpp::XPtr<Geography> geography(this->provider.feature());
+ geography->Export(handler, WKReader::PART_ID_NONE);
+ }
+
+ this->handler->nextFeatureEnd(featureId);
+ }
+
+private:
+ WKRcppSEXPProvider& provider;
+};
+
+#endif
--- /dev/null
+library(s2)
+
+u = s2_union(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))",
+ s2_options(snap = s2_snap_level(30))
+)
+s2_area(u, radius = 1)
+s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) +
+s2_area("POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", radius = 1) -
+s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1)
+s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1)
+s2_area("POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", radius = 1)
+s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1)
+
+df = s2_difference(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))",
+ s2_options(snap = s2_snap_level(30))
+)
+s2_area(df, radius = 1) -
+ (s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) -
+ s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1))
--- /dev/null
+> library(s2)
+> s2_set_snaplevel(30) # for windows i386
+[1] -1
+> u = s2_union(
++ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
++ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))"
++ )
+> s2_area(u, radius = 1)
+[1] 0.05284581
+> s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) +
++ s2_area("POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", radius = 1) -
++ s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1)
+[1] 0.04910511
+> s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1)
+[1] 0.03038216
+> s2_area("POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", radius = 1)
+[1] 0.03002974
+> s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1)
+[1] 0.01130679
+>
+> df = s2_difference(
++ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
++ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))"
++ )
+> s2_area(df, radius = 1) -
++ (s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) -
++ s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1))
+[1] 0.003740703
+>
--- /dev/null
+library(testthat)
+library(s2)
+
+test_check("s2")
--- /dev/null
+
+test_that("s2_data_country() works", {
+ expect_is(s2_data_countries("Germany"), "s2_geography")
+ expect_length(s2_data_countries("Germany"), 1)
+
+ expect_is(s2_data_countries("Europe"), "s2_geography")
+ expect_length(s2_data_countries("Europe"), 39)
+
+ expect_is(s2_data_countries(), "s2_geography")
+ expect_length(s2_data_countries(), 177)
+})
+
+test_that("s2_data_timezone() works", {
+ expect_is(s2_data_timezones(), "s2_geography")
+ expect_length(s2_data_timezones(), 120)
+
+ expect_is(s2_data_timezones(-4), "s2_geography")
+ expect_length(s2_data_timezones(-4), 3)
+
+ expect_is(s2_data_timezones(-15, 15), "s2_geography")
+ expect_length(s2_data_timezones(-15, 15), 120)
+})
+
+test_that("s2_data_cities() works", {
+ expect_is(s2_data_cities(), "s2_geography")
+ expect_length(s2_data_cities(), 243)
+
+ expect_is(s2_data_cities("Cairo"), "s2_geography")
+ expect_length(s2_data_cities("Cairo"), 1)
+})
--- /dev/null
+
+test_that("s2_is_collection works", {
+ expect_identical(s2_is_collection(NA_character_), NA)
+ expect_false(s2_is_collection("POINT (-64 45)"))
+ expect_false(s2_is_collection("POINT EMPTY"))
+ expect_true(s2_is_collection("MULTIPOINT ((0 0), (1 1))"))
+
+ expect_false(s2_is_collection("LINESTRING (0 0, 1 1)"))
+ expect_true(s2_is_collection("MULTILINESTRING ((0 0, 1 1), (2 2, 3 3))"))
+ expect_false(s2_is_collection("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))"))
+ expect_true(
+ s2_is_collection("MULTIPOLYGON (
+ ((40 40, 20 45, 45 30, 40 40)),
+ ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20))
+ )")
+ )
+})
+
+test_that("s2_dimension works", {
+ expect_identical(s2_dimension(NA_character_), NA_integer_)
+ expect_identical(s2_dimension("POINT EMPTY"), 0L)
+ expect_identical(s2_dimension("LINESTRING EMPTY"), 1L)
+ expect_identical(s2_dimension("POLYGON EMPTY"), 2L)
+})
+
+test_that("s2_num_points works", {
+ expect_identical(s2_num_points(NA_character_), NA_integer_)
+ expect_identical(s2_num_points("POINT (-64 45)"), 1L)
+ expect_identical(s2_num_points("POINT EMPTY"), 0L)
+ expect_identical(s2_num_points("LINESTRING (0 0, 1 1)"), 2L)
+ expect_identical(s2_num_points("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))"), 4L)
+})
+
+test_that("s2_is_empty works", {
+ expect_identical(s2_is_empty(NA_character_), NA)
+ expect_false(s2_is_empty("POINT (-64 45)"))
+ expect_true(s2_is_empty("POINT EMPTY"))
+ expect_false(s2_is_empty("LINESTRING (0 0, 1 1)"))
+ expect_true(s2_is_empty("LINESTRING EMPTY"))
+ expect_false(s2_is_empty("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))"))
+ expect_true(s2_is_empty("POLYGON EMPTY"))
+})
+
+test_that("s2_area works", {
+ expect_identical(s2_area(NA_character_), NA_real_)
+ expect_identical(s2_area("POINT (-64 45)"), 0)
+ expect_identical(s2_area("POINT EMPTY"), 0)
+ expect_identical(s2_area("LINESTRING (0 0, 1 1)"), 0)
+ expect_identical(s2_area("POLYGON EMPTY"), 0)
+ expect_identical(s2_area("POLYGON ((0 0, 90 0, 0 90, 0 0))", radius = 1), 4 * pi / 8)
+ # make sure the radius is squared!
+ expect_true(
+ abs(s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 180 / pi) - 100) < 0.27
+ )
+})
+
+test_that("s2_length works", {
+ expect_identical(s2_length(NA_character_), NA_real_)
+ expect_identical(s2_length("POINT (-64 45)"), 0)
+ expect_identical(s2_length("POINT EMPTY"), 0)
+ expect_identical(s2_length("LINESTRING EMPTY"), 0)
+ expect_identical(s2_length("LINESTRING (0 0, 0 1)", radius = 180 / pi), 1)
+ expect_identical(s2_length("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))"), 0)
+})
+
+test_that("s2_perimeter works", {
+ expect_identical(s2_perimeter(NA_character_), NA_real_)
+ expect_identical(s2_perimeter("POINT (-64 45)"), 0)
+ expect_identical(s2_perimeter("POINT EMPTY"), 0)
+ expect_identical(s2_perimeter("LINESTRING EMPTY"), 0)
+ expect_identical(s2_perimeter("LINESTRING (0 0, 1 1)"), 0)
+
+ # there is some error here because of the way this is calculated involves
+ # some round-tripping through lat/lon
+ expect_true(
+ abs(s2_perimeter("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 180 / pi) - 40) < 0.155
+ )
+})
+
+test_that("s2_x and s2_y works", {
+ expect_identical(s2_x(NA_character_), NA_real_)
+ expect_identical(s2_y(NA_character_), NA_real_)
+ expect_equal(s2_x("POINT (-64 45)"), -64)
+ expect_equal(s2_y("POINT (-64 45)"), 45)
+ expect_identical(s2_x("POINT EMPTY"), NA_real_)
+ expect_identical(s2_y("POINT EMPTY"), NA_real_)
+ expect_error(s2_x("LINESTRING EMPTY"), "Can't compute")
+ expect_error(s2_y("LINESTRING EMPTY"), "Can't compute")
+ expect_error(s2_x("POLYGON EMPTY"), "Can't compute")
+ expect_error(s2_y("POLYGON EMPTY"), "Can't compute")
+})
+
+test_that("s2_distance works", {
+ expect_equal(
+ s2_distance("POINT (0 0)", "POINT (90 0)", radius = 180 / pi),
+ 90
+ )
+
+ expect_equal(
+ s2_distance("POINT (0 0)", "LINESTRING (90 0, 91 0)", radius = 180 / pi),
+ 90
+ )
+
+ expect_equal(
+ s2_distance("POINT (0 0)", "POLYGON ((90 0, 91 0, 92 1, 90 0))", radius = 180 / pi),
+ 90
+ )
+
+ expect_identical(s2_distance("POINT (0 0)", NA_character_), NA_real_)
+ expect_identical(s2_distance(NA_character_, "POINT (0 0)"), NA_real_)
+ expect_identical(s2_distance("POINT (0 0)", "POINT EMPTY"), NA_real_)
+ expect_identical(s2_distance("POINT EMPTY", "POINT (0 0)"), NA_real_)
+})
+
+test_that("s2_max_distance works", {
+ expect_equal(
+ s2_max_distance("POINT (0 0)", "POINT (90 0)", radius = 180 / pi),
+ 90
+ )
+
+ expect_equal(
+ s2_max_distance("POINT (0 0)", "LINESTRING (90 0, 91 0)", radius = 180 / pi),
+ 91
+ )
+
+ expect_equal(
+ s2_max_distance("POINT (0 0)", "POLYGON ((90 0, 91 0, 89 1, 90 0))", radius = 180 / pi),
+ 91
+ )
+
+ expect_identical(s2_max_distance("POINT (0 0)", NA_character_), NA_real_)
+ expect_identical(s2_max_distance(NA_character_, "POINT (0 0)"), NA_real_)
+ expect_identical(s2_max_distance("POINT (0 0)", "POINT EMPTY"), NA_real_)
+ expect_identical(s2_max_distance("POINT EMPTY", "POINT (0 0)"), NA_real_)
+})
--- /dev/null
+
+test_that("s2_bounds_cap works", {
+ cap_ant <- s2_data_countries("Antarctica")
+ expect_is(s2_bounds_cap(cap_ant), "data.frame")
+ expect_identical(nrow(s2_bounds_cap(cap_ant)), 1L)
+ expect_true(s2_bounds_cap(cap_ant)$lat < -80)
+ expect_true(s2_bounds_cap(cap_ant)$angle > 20)
+
+ expect_identical(nrow(s2_bounds_cap(s2_data_countries(c("Antarctica", "Netherlands")))), 2L)
+
+ expect_true(s2_bounds_cap(s2_data_countries("Netherlands"))$angle < 2)
+ expect_true(s2_bounds_cap(s2_data_countries("Fiji"))$angle < 2)
+})
+
+test_that("s2_bounds_rect works", {
+ rect_ant <- s2_bounds_rect(s2_data_countries("Antarctica"))
+ expect_is(rect_ant, "data.frame")
+ expect_named(rect_ant, c("lng_lo", "lat_lo", "lng_hi", "lat_hi"))
+ expect_identical(nrow(s2_bounds_rect(s2_data_countries(c("Antarctica", "Netherlands")))), 2L)
+ expect_equal(rect_ant$lng_lo, -180)
+ expect_equal(rect_ant$lng_hi, 180)
+ expect_equal(rect_ant$lat_lo, -90)
+ expect_true(rect_ant$lat_hi < -60)
+
+ expect_identical(nrow(s2_bounds_rect(s2_data_countries(c("Antarctica", "Netherlands")))), 2L)
+
+ rect_nl <- s2_bounds_rect(s2_data_countries("Netherlands"))
+ expect_true((rect_nl$lng_hi - rect_nl$lng_lo) < 4)
+
+ rect_fiji <- s2_bounds_rect(s2_data_countries("Fiji"))
+ expect_true(rect_fiji$lng_hi < rect_fiji$lng_lo)
+
+ rect_multipoint <- s2_bounds_rect("MULTIPOINT(-179 0,179 1,-180 10)")
+ expect_equal(rect_multipoint$lat_lo, 0)
+ expect_equal(rect_multipoint$lat_hi, 10)
+ expect_equal(rect_multipoint$lng_lo, 179)
+ expect_equal(rect_multipoint$lng_hi, -179)
+
+ rect_linestring <- s2_bounds_rect("LINESTRING(-179 0,179 1)")
+ expect_equal(rect_linestring$lat_lo, 0)
+ expect_equal(rect_linestring$lat_hi, 1)
+ expect_equal(rect_linestring$lng_lo, 179)
+ expect_equal(rect_linestring$lng_hi, -179)
+})
--- /dev/null
+
+test_that("s2_geog_point() works", {
+ expect_wkt_equal(s2_geog_point(-64, 45), "POINT (-64 45)")
+})
+
+test_that("s2_make_line() works", {
+ expect_wkt_equal(
+ s2_make_line(c(-64, 8), c(45, 71)),
+ "LINESTRING (-64 45, 8 71)"
+ )
+
+ # check separation using feature_id
+ expect_wkt_equal(
+ s2_make_line(
+ c(-64, 8, -27, -27), c(45, 71, 0, 45),
+ feature_id = c(1, 1, 2, 2)
+ ),
+ c("LINESTRING (-64 45, 8 71)", "LINESTRING (-27 0, -27 45)")
+ )
+})
+
+test_that("s2_make_polygon() works", {
+ expect_wkt_equal(
+ s2_make_polygon(c(-45, 8, 0), c(64, 71, 90)),
+ "POLYGON ((-45 64, 8 71, 0 90, -45 64))"
+ )
+ # check that loops can be open or closed
+ expect_wkt_equal(
+ s2_make_polygon(c(-45, 8, 0, -45), c(64, 71, 90, 64)),
+ "POLYGON ((-45 64, 8 71, 0 90, -45 64))"
+ )
+
+ # check feature/ring separation
+ expect_wkt_equal(
+ s2_make_polygon(
+ c(20, 10, 10, 30, 45, 30, 20, 20, 40, 20, 45),
+ c(35, 30, 10, 5, 20, 20, 15, 25, 40, 45, 30),
+ feature_id = c(rep(1, 8), rep(2, 3)),
+ ring_id = c(1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1)
+ ),
+ c(
+ "POLYGON ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20))",
+ "POLYGON ((40 40, 20 45, 45 30, 40 40))"
+ )
+ )
+})
+
+test_that("s2_geog_from_wkt() works", {
+ expect_wkt_equal(s2_geog_from_text("POINT (-64 45)"), "POINT (-64 45)")
+})
+
+test_that("s2_geog_from_wkb() works", {
+ expect_wkt_equal(s2_geog_from_wkb(as_wkb("POINT (-64 45)")), "POINT (-64 45)")
+})
+
+test_that("s2_as_text() works", {
+ expect_identical(
+ s2_as_text("POINT (0.1234567890123456 0.1234567890123456)"),
+ "POINT (0.1234567890123456 0.1234567890123456)"
+ )
+})
+
+test_that("s2_as_binary() works", {
+ expect_identical(
+ s2_as_binary("POINT (0 0)", endian = 1),
+ structure(
+ list(
+ as.raw(
+ c(0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00
+ )
+ )
+ ),
+ class = "blob"
+ )
+ )
+})
+
+test_that("s2_as_binary works on (multi)polygons", {
+ geog <- s2_data_countries()
+ wkb <- s2_as_binary(geog)
+
+ expect_identical(
+ sum(vapply(wkb, length, integer(1))),
+ 173318L
+ )
+ expect_identical(length(wkb), length(geog))
+})
+
+test_that("polygon constructors respect oriented and check arguments", {
+ polygon_with_bad_hole_wkt <- "POLYGON (
+ (20 35, 10 30, 10 10, 30 5, 45 20, 20 35),
+ (30 20, 20 25, 20 15, 30 20)
+ )"
+ polygon_with_bad_hole_wkb <- wk::as_wkb(polygon_with_bad_hole_wkt)
+ polygon_with_bad_hole_df <- data.frame(
+ x = c(20, 10, 10, 30, 45, 30, 20, 20),
+ y = c(35, 30, 10, 5, 20, 20, 25, 15),
+ ring_id = c(1, 1, 1, 1, 1, 2, 2, 2)
+ )
+
+ expect_false(
+ s2_intersects(
+ s2_geog_from_text(polygon_with_bad_hole_wkt, oriented = FALSE),
+ "POINT (23 19.5)"
+ )
+ )
+ expect_false(
+ s2_intersects(
+ s2_geog_from_wkb(polygon_with_bad_hole_wkb, oriented = FALSE),
+ "POINT (23 19.5)"
+ )
+ )
+ expect_false(
+ s2_intersects(
+ with(
+ polygon_with_bad_hole_df,
+ s2_make_polygon(x, y, ring_id = ring_id, oriented = FALSE)
+ ),
+ "POINT (23 19.5)"
+ )
+ )
+
+ expect_error(
+ s2_intersects(
+ s2_geog_from_text(polygon_with_bad_hole_wkt, oriented = TRUE, check = TRUE),
+ "POINT (23 19.5)"
+ ),
+ "Inconsistent loop orientations"
+ )
+ expect_error(
+ s2_intersects(
+ s2_geog_from_wkb(polygon_with_bad_hole_wkb, oriented = TRUE, check = TRUE),
+ "POINT (23 19.5)"
+ ),
+ "Inconsistent loop orientations"
+ )
+ expect_error(
+ s2_intersects(
+ with(
+ polygon_with_bad_hole_df,
+ s2_make_polygon(x, y, ring_id = ring_id, oriented = TRUE, check = TRUE)
+ ),
+ "POINT (23 19.5)"
+ ),
+ "Inconsistent loop orientations"
+ )
+
+ expect_silent(
+ s2_intersects(
+ s2_geog_from_text(polygon_with_bad_hole_wkt, oriented = TRUE, check = FALSE),
+ "POINT (23 19.5)"
+ )
+ )
+ expect_silent(
+ s2_intersects(
+ s2_geog_from_wkb(polygon_with_bad_hole_wkb, oriented = TRUE, check = FALSE),
+ "POINT (23 19.5)"
+ )
+ )
+ expect_silent(
+ s2_intersects(
+ with(
+ polygon_with_bad_hole_df,
+ s2_make_polygon(x, y, ring_id = ring_id, oriented = TRUE, check = FALSE)
+ ),
+ "POINT (23 19.5)"
+ )
+ )
+})
--- /dev/null
+
+test_that("s2_earth_radius_meters works", {
+ expect_is(s2_earth_radius_meters(), "numeric")
+ expect_length(s2_earth_radius_meters(), 1)
+})
--- /dev/null
+
+test_that("s2_geography class works", {
+ geog <- new_s2_xptr(list(NULL), class = "s2_geography")
+ expect_output(print(geog), "s2_geography")
+ expect_identical(as_s2_geography(geog), geog)
+
+ # subset assignment
+ geog2 <- geog
+ geog2[1] <- geog
+ expect_identical(geog2, geog)
+
+ geog2 <- geog
+ geog2[[1]] <- geog
+ expect_identical(geog2, geog)
+})
+
+test_that("s2_geography vectors can't have other types of objects concatenated or asssigned", {
+ geog <- new_s2_xptr(list(NULL), class = "s2_geography")
+ expect_is(c(geog, geog), "s2_geography")
+ expect_error(c(geog, new_s2_xptr(list(), class = "some_other_class")), "All items must inherit")
+ expect_error(geog[1] <- new_s2_xptr(list(NULL), class = "some_other_class"), "no applicable method")
+ expect_error(geog[[1]] <- new_s2_xptr(list(NULL), class = "some_other_class"), "no applicable method")
+})
+
+test_that("s2_geography vectors can be created from s2_lnglat and s2_point", {
+ expect_wkt_equal(as_s2_geography(s2_lnglat(-64, 45)), "POINT (-64 45)")
+ expect_wkt_equal(as_s2_geography(as_s2_point(s2_lnglat(-64, 45))), "POINT (-64 45)")
+})
+
+test_that("s2_geography vectors can be created from WKB and WKT", {
+ wkb_point <- wk::as_wkb("POINT (-64 45)")
+ expect_output(print(as_s2_geography(wkb_point)), "<POINT \\(-64 45\\)>")
+
+ wkt_point <- wk::as_wkt("POINT (-64 45)")
+ expect_output(print(as_s2_geography(wkt_point)), "<POINT \\(-64 45\\)>")
+
+ # also test other classes commonly used to signify WKB or WKT
+ expect_output(print(as_s2_geography(structure(wkb_point, class = "WKB")), "<POINT \\(-64 45\\)>"))
+ expect_output(print(as_s2_geography(structure(wkb_point, class = "blob")), "<POINT \\(-64 45\\)>"))
+})
+
+test_that("s2_geography can be exported to WKB/WKT", {
+ expect_wkt_equal(
+ wk::as_wkb(as_s2_geography("POINT (-64 45)")),
+ wk::as_wkb("POINT (-64 45)"),
+ precision = 10
+ )
+ expect_wkt_equal(
+ wk::as_wkt(as_s2_geography("POINT (-64 45)")),
+ wk::as_wkt("POINT (-64 45)"),
+ precision = 10
+ )
+})
+
+test_that("s2_geography vectors can be created from wkt", {
+ expect_output(print(as_s2_geography("POINT (-64 45)")), "<POINT \\(-64 45\\)>")
+ expect_output(print(as_s2_geography("POINT EMPTY")), "<POINT EMPTY>")
+ expect_output(
+ print(as_s2_geography("MULTIPOINT ((-64 45), (30 10))")),
+ "<MULTIPOINT \\(\\(-64 45\\), \\(30 10\\)\\)>"
+ )
+
+ expect_output(
+ print(as_s2_geography("LINESTRING (-64 45, 0 0)")),
+ "<LINESTRING \\(-64 45, 0 0\\)>"
+ )
+ expect_output(
+ print(as_s2_geography("LINESTRING EMPTY")),
+ "<LINESTRING EMPTY>"
+ )
+ expect_output(
+ print(as_s2_geography("MULTILINESTRING ((-64 45, 0 0), (0 1, 2 3))")),
+ "<MULTILINESTRING \\(\\(-64 45, 0 0), \\(0 1, 2 3\\)\\)>"
+ )
+
+ expect_output(print(as_s2_geography("POLYGON EMPTY"), "<POLYGON EMPTY>"))
+ expect_output(
+ print(as_s2_geography("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))")),
+ "<POLYGON \\(\\(0 0, 10 0, 10 10"
+ )
+ expect_output(
+ print(as_s2_geography("MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0)))")),
+ "<POLYGON \\(\\(0 0, 10 0, 10 10"
+ )
+ expect_output(
+ print(
+ as_s2_geography("MULTIPOLYGON (
+ ((40 40, 20 45, 45 30, 40 40)),
+ ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20))
+ )")
+ ),
+ "<MULTIPOLYGON"
+ )
+
+ expect_output(
+ print(as_s2_geography("GEOMETRYCOLLECTION (POINT (-64 45))")),
+ "<GEOMETRYCOLLECTION \\(POINT \\(-64 45\\)\\)>"
+ )
+
+ expect_output(
+ print(
+ as_s2_geography(
+ "GEOMETRYCOLLECTION (
+ POINT (30 10),
+ MULTIPOINT (11 12, 12 13),
+ LINESTRING (40 40, 40 41),
+ MULTILINESTRING ((-10 -12, -12 -13), (-15 -15, -16 -16)),
+ POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0)),
+ MULTIPOLYGON (((0 0, -10 0, -10 -10, 0 -10, 0 0))),
+ GEOMETRYCOLLECTION (POINT (-100 0), MULTIPOINT(-101 0, -102 0))
+ )"
+ ),
+ max_coords = 100
+ ),
+ paste0(
+ "GEOMETRYCOLLECTION.*?POINT.*?MULTIPOINT.*?LINESTRING.*?MULTILINESTRING.*?",
+ "POLYGON.*?POLYGON.*?GEOMETRYCOLLECTION.*?POINT.*?MULTIPOINT"
+ )
+ )
+
+ expect_output(print(as_s2_geography("GEOMETRYCOLLECTION EMPTY")), "<GEOMETRYCOLLECTION EMPTY>")
+})
+
+test_that("nested ring depths are correctly exported", {
+ # polygon with hole
+ expect_output(
+ print(
+ as_s2_geography("MULTIPOLYGON (
+ ((40 40, 20 45, 45 30, 40 40)),
+ (
+ (20 35, 10 30, 10 10, 30 5, 45 20, 20 35),
+ (30 20, 20 15, 20 25, 30 20)
+ )
+ )"),
+ max_coords = 100
+ ),
+ "\\(20 35, 10 30, 10 10, 30 5, 45 20, 20 35\\), \\(30 20, 20 15, 20 25, 30 20"
+ )
+
+ # polygon with a hole in a hole!
+ expect_output(
+ print(
+ as_s2_geography("MULTIPOLYGON (
+ ((40 40, 20 45, 45 30, 40 40)),
+ (
+ (20 35, 10 30, 10 10, 30 5, 45 20, 20 35),
+ (30 20, 20 15, 20 25, 30 20),
+ (27 21, 21 21, 21 16, 27 21)
+ )
+ )"),
+ max_coords = 100
+ ),
+ "30 20, 20 15, 20 25, 30 20\\), \\(27 21, 21 21, 21 16, 27 21"
+ )
+})
+
+test_that("polygons with holes are interpreted as such by S2", {
+ expect_true(
+ s2_intersects(
+ "MULTIPOLYGON (
+ ((40 40, 20 45, 45 30, 40 40)),
+ (
+ (20 35, 10 30, 10 10, 30 5, 45 20, 20 35),
+ (30 20, 20 15, 20 25, 30 20),
+ (27 21, 21 21, 21 16, 27 21)
+ )
+ )",
+ "POINT (23 19.5)"
+ )
+ )
+
+ expect_false(
+ s2_intersects(
+ "MULTIPOLYGON (
+ ((40 40, 20 45, 45 30, 40 40)),
+ (
+ (20 35, 10 30, 10 10, 30 5, 45 20, 20 35),
+ (30 20, 20 15, 20 25, 30 20)
+ )
+ )",
+ "POINT (23 19.5)"
+ )
+ )
+})
+
+test_that("polygon construction works with oriented = TRUE and oriented = FALSE", {
+ polygon_with_bad_hole_nested <- as_s2_geography("MULTIPOLYGON (
+ ((40 40, 20 45, 45 30, 40 40)),
+ (
+ (20 35, 10 30, 10 10, 30 5, 45 20, 20 35),
+ (30 20, 20 25, 20 15, 30 20)
+ )
+ )", oriented = FALSE)
+
+ expect_false(s2_intersects(polygon_with_bad_hole_nested, "POINT (23 19.5)"))
+
+ expect_error(
+ as_s2_geography("MULTIPOLYGON (
+ ((40 40, 20 45, 45 30, 40 40)),
+ (
+ (20 35, 10 30, 10 10, 30 5, 45 20, 20 35),
+ (30 20, 20 25, 20 15, 30 20)
+ )
+ )", oriented = TRUE, check = TRUE),
+ "Inconsistent loop orientations"
+ )
+
+ expect_silent(
+ as_s2_geography("MULTIPOLYGON (
+ ((40 40, 20 45, 45 30, 40 40)),
+ (
+ (20 35, 10 30, 10 10, 30 5, 45 20, 20 35),
+ (30 20, 20 25, 20 15, 30 20)
+ )
+ )", oriented = TRUE, check = FALSE)
+ )
+})
+
+test_that("Full polygons work", {
+ expect_true(s2_intersects(as_s2_geography(TRUE), "POINT(0 1)"))
+ expect_wkt_equal(s2_difference(as_s2_geography(TRUE), "POINT(0 1)"), "POLYGON ((0 -90, 0 -90))")
+})
--- /dev/null
+
+test_that("s2_lnglat objects can be created from and converted back to R objects", {
+ # in
+ expect_is(s2_lnglat(45, 64), "s2_lnglat")
+ expect_length(s2_lnglat(45, 64), 1)
+ expect_is(as_s2_lnglat(matrix(45, 64, ncol = 2)), "s2_lnglat")
+ lnglat <- s2_lnglat(45, 64)
+ expect_identical(as_s2_lnglat(lnglat), lnglat)
+ expect_identical(
+ as.data.frame(as_s2_lnglat(s2_point(1, 0, 0))),
+ as.data.frame(s2_lnglat(0, 0))
+ )
+
+ # subset assignment
+ lnglat2 <- lnglat
+ lnglat2[1] <- lnglat
+ expect_identical(lnglat2, lnglat)
+
+ lnglat2 <- lnglat
+ lnglat2[[1]] <- lnglat
+ expect_identical(lnglat2, lnglat)
+
+ # out
+ expect_identical(as.data.frame(s2_lnglat(-64, 45)), data.frame(lng = -64, lat = 45))
+ expect_identical(as.matrix(s2_lnglat(-64, 45)), as.matrix(data.frame(lng = -64, lat = 45)))
+
+ # zero-length in and out
+ expect_length(s2_lnglat(double(), double()), 0)
+ expect_identical(
+ as.data.frame(s2_lnglat(double(), double())),
+ data.frame(lng = double(), lat = double())
+ )
+
+ # NAs and NULLs
+ expect_identical(
+ as.data.frame(s2_lnglat(double(), double())[NA]),
+ data.frame(lng = NA_real_, lat = NA_real_)
+ )
+})
+
+test_that("s2_lnglat can be imported/exported to/from wkb and wkt", {
+ wkb_point <- wk::as_wkb("POINT (-64 45)")
+ expect_wkt_equal(wk::as_wkb(s2_lnglat(-64, 45)), wkb_point)
+ expect_wkt_equal(wk::as_wkt(s2_lnglat(-64, 45)), wk::wkt("POINT (-64 45)"))
+ expect_identical(wk::as_wkt(s2_lnglat(NA, NA)), wk::wkt("POINT EMPTY"))
+
+ expect_equal(
+ as.data.frame(as_s2_lnglat(wk::as_wkb(c("POINT EMPTY", "POINT (-64 45)")))),
+ data.frame(lng = c(NA, -64), lat = c(NA, 45))
+ )
+
+ expect_equal(
+ as.data.frame(as_s2_lnglat(c("POINT EMPTY", "POINT (-64 45)"))),
+ data.frame(lng = c(NA, -64), lat = c(NA, 45))
+ )
+
+ expect_equal(
+ as.data.frame(as_s2_lnglat(wk::wkt(c("POINT EMPTY", "POINT (-64 45)")))),
+ data.frame(lng = c(NA, -64), lat = c(NA, 45))
+ )
+
+ expect_error(as_s2_lnglat(wk::wkt("LINESTRING EMPTY")), "non-point")
+ expect_error(as_s2_lnglat(wk::as_wkb("LINESTRING EMPTY")), "non-point")
+})
+
+test_that("s2_lnglat vectors can't have other types of objects concatenated or asssigned", {
+ lnglat <- new_s2_xptr(list(NULL), class = "s2_lnglat")
+ expect_is(c(lnglat, lnglat), "s2_lnglat")
+ expect_error(c(lnglat, new_s2_xptr(list(), class = "some_other_class")), "All items must inherit")
+ expect_error(lnglat[1] <- new_s2_xptr(list(NULL), class = "some_other_class"), "no applicable method")
+ expect_error(lnglat[[1]] <- new_s2_xptr(list(NULL), class = "some_other_class"), "no applicable method")
+})
+
+test_that("s2_lnglat can be imported from s2_geography", {
+ expect_equal(
+ as.data.frame(as_s2_lnglat(as_s2_geography("POINT (-64 45)"))),
+ data.frame(lng = -64, lat = 45)
+ )
+})
+
+test_that("s2_lnglat can be imported from wkb", {
+ wkb_point <- wk::as_wkb("POINT (-64 45)")
+
+ expect_equal(
+ as.data.frame(as_s2_lnglat(wkb_point)),
+ data.frame(lng = -64, lat = 45)
+ )
+})
+
+test_that("s2_lnglat objects can be printed", {
+ expect_output(print(s2_lnglat(-64, 45)), "s2_lnglat")
+})
--- /dev/null
+
+test_that("s2_closest|farthest_feature() works", {
+ cities <- s2_data_cities("London")
+ countries <- s2_data_countries()
+
+ # with zero length y, results will all be empty
+ expect_identical(s2_closest_feature(cities, character(0)), rep_len(NA_integer_, length(cities)))
+
+ # should correctly identify that London is closest to United Kingdom
+ country_match <- s2_closest_feature(cities, countries)
+ expect_identical(s2_data_tbl_countries$name[country_match], "United Kingdom")
+
+ country_match_farthest <- s2_farthest_feature(cities, countries)
+ expect_identical(s2_data_tbl_countries$name[country_match_farthest], "New Zealand")
+})
+
+test_that("matrix predicates work", {
+ expect_identical(
+ s2_contains_matrix(
+ "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))",
+ c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)"),
+ ),
+ list(2L)
+ )
+
+ expect_identical(
+ s2_within_matrix(
+ c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)"),
+ "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))"
+ ),
+ list(integer(0), 1L, integer(0))
+ )
+
+ expect_identical(
+ s2_covers_matrix(
+ "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))",
+ c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)"),
+ ),
+ list(2L)
+ )
+
+ expect_identical(
+ s2_covered_by_matrix(
+ c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)"),
+ "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))"
+ ),
+ list(integer(0), 1L, integer(0))
+ )
+
+ expect_identical(
+ s2_intersects_matrix(
+ c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)"),
+ "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))"
+ ),
+ list(integer(0), 1L, integer(0))
+ )
+
+ expect_identical(
+ s2_disjoint_matrix(
+ c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)"),
+ "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))"
+ ),
+ list(1L, integer(0), 1L)
+ )
+
+ expect_identical(
+ s2_equals_matrix(
+ c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)",
+ "POLYGON ((1 0, 1 1, 0 1, 0 0, 1 0))"),
+ "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))"
+ ),
+ list(integer(0), integer(0), integer(0), 1L)
+ )
+
+ expect_identical(
+ s2_touches_matrix(
+ c("POINT (0.5 0.5)", "POINT (0 0)", "POINT (-0.5 -0.5)"),
+ "POLYGON ((0 0, 0 1, 1 1, 0 0))"
+ ),
+ list(integer(0), 1L, integer(0))
+ )
+
+ expect_identical(
+ s2_dwithin_matrix(
+ c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)"),
+ "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))",
+ 0
+ ),
+ list(integer(0), 1L, integer(0))
+ )
+ expect_identical(
+ s2_dwithin_matrix(
+ c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)"),
+ "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))",
+ s2_earth_radius_meters()
+ ),
+ list(1L, 1L, 1L)
+ )
+})
+
+test_that("s2_(max_)?distance_matrix() works", {
+ x <- c("POINT (0 0)", "POINT (0 90)")
+ y <- c("POINT (180 0)", "POINT (0 -90)", "POINT (0 0)")
+
+ expect_equal(
+ s2_distance_matrix(x, x, radius = 180 / pi),
+ matrix(c(0, 90, 90, 0), ncol = 2)
+ )
+ expect_equal(
+ s2_distance_matrix(x, y, radius = 180 / pi),
+ matrix(c(180, 90, 90, 180, 0, 90), ncol = 3)
+ )
+
+ # max distance is the same for points
+ expect_equal(
+ s2_max_distance_matrix(x, x, radius = 180 / pi),
+ matrix(c(0, 90, 90, 0), ncol = 2)
+ )
+
+ expect_equal(
+ s2_max_distance_matrix(x, y, radius = 180 / pi),
+ matrix(c(180, 90, 90, 180, 0, 90), ncol = 3)
+ )
+
+ # NA handling for both rows and cols
+ y[2] <- NA
+ expect_true(all(is.na(s2_distance_matrix(x, y)[, 2])))
+ expect_true(all(is.na(s2_max_distance_matrix(x, y)[, 2])))
+
+ x[2] <- NA
+ expect_true(all(is.na(s2_distance_matrix(x, y)[2, ])))
+ expect_true(all(is.na(s2_max_distance_matrix(x, y)[2, ])))
+})
+
+test_that("s2_may_intersect_matrix() works", {
+ countries <- s2_data_countries()
+ timezones <- s2_data_timezones()
+
+ maybe_intersects <- s2_may_intersect_matrix(countries, timezones)
+ intersects <- s2_intersects_matrix_brute_force(countries, timezones)
+ for (i in seq_along(countries)) {
+ expect_identical(setdiff(intersects[[!!i]], maybe_intersects[[!!i]]), integer(0))
+ }
+})
+
+test_that("indexed matrix predicates return the same thing as brute-force comparisons", {
+ countries <- s2_data_countries()
+ timezones <- s2_data_timezones()
+
+ # contains
+ expect_identical(
+ s2_contains_matrix(countries, countries),
+ s2_contains_matrix_brute_force(countries, countries)
+ )
+ expect_identical(
+ s2_contains_matrix(timezones, countries),
+ s2_contains_matrix_brute_force(timezones, countries)
+ )
+
+ # within
+ expect_identical(
+ s2_within_matrix(countries, countries),
+ s2_within_matrix_brute_force(countries, countries)
+ )
+ expect_identical(
+ s2_within_matrix(timezones, countries),
+ s2_within_matrix_brute_force(timezones, countries)
+ )
+
+ # covers
+ expect_identical(
+ s2_covers_matrix(countries, countries),
+ s2_covers_matrix_brute_force(countries, countries)
+ )
+ expect_identical(
+ s2_covers_matrix(timezones, countries),
+ s2_covers_matrix_brute_force(timezones, countries)
+ )
+
+ # covered by
+ expect_identical(
+ s2_covered_by_matrix(countries, countries),
+ s2_covered_by_matrix_brute_force(countries, countries)
+ )
+ expect_identical(
+ s2_covered_by_matrix(timezones, countries),
+ s2_covered_by_matrix_brute_force(timezones, countries)
+ )
+
+ # intersects
+ expect_identical(
+ s2_intersects_matrix(countries, countries),
+ s2_intersects_matrix_brute_force(countries, countries)
+ )
+ expect_identical(
+ s2_intersects_matrix(timezones, countries),
+ s2_intersects_matrix_brute_force(timezones, countries)
+ )
+
+ # disjoint
+ expect_identical(
+ s2_disjoint_matrix(countries, countries),
+ s2_disjoint_matrix_brute_force(countries, countries)
+ )
+ expect_identical(
+ s2_disjoint_matrix(timezones, countries),
+ s2_disjoint_matrix_brute_force(timezones, countries)
+ )
+
+ # equals
+ expect_identical(
+ s2_equals_matrix(countries, countries),
+ s2_equals_matrix_brute_force(countries, countries)
+ )
+ expect_identical(
+ s2_equals_matrix(timezones, countries),
+ s2_equals_matrix_brute_force(timezones, countries)
+ )
+})
--- /dev/null
+
+test_that("s2_options() works", {
+ expect_is(s2_options(), "s2_options")
+})
+
+test_that("s2_options() errors are readable", {
+ expect_error(s2_intersects("POINT EMPTY", "POINT EMPTY", options = NULL), "must be created using")
+ expect_error(s2_options(model = "not a model"), "must be one of")
+ expect_error(s2_options(snap_radius = 100), "radius is too large")
+ expect_error(s2_snap_level(31), "between 1 and 30")
+})
--- /dev/null
+
+test_that("s2_point objects can be created from and converted back to R objects", {
+ # in
+ expect_is(s2_point(1, 2, 3), "s2_point")
+ expect_length(s2_point(1, 2, 3), 1)
+ expect_is(as_s2_point(matrix(1, 2, 3, ncol = 3)), "s2_point")
+ point <- s2_point(1, 2, 3)
+ expect_identical(as_s2_point(point), point)
+ expect_identical(
+ as.data.frame(as_s2_point(s2_lnglat(0, 0))),
+ as.data.frame(s2_point(1, 0, 0))
+ )
+
+ # subset assignment
+ point2 <- point
+ point2[1] <- point
+ expect_identical(point2, point)
+
+ point2 <- point
+ point2[[1]] <- point
+ expect_identical(point2, point)
+
+ # out
+ expect_identical(as.data.frame(s2_point(1, 2, 3)), data.frame(x = 1, y = 2, z = 3))
+ expect_identical(as.matrix(s2_point(1, 2, 3)), as.matrix(data.frame(x = 1, y = 2, z = 3)))
+
+ # zero-length
+ expect_length(s2_point(double(), double(), double()), 0)
+ expect_identical(
+ as.data.frame(s2_point(double(), double(), double())),
+ data.frame(x = double(), y = double(), z = double())
+ )
+
+ expect_identical(
+ as.data.frame(s2_point(double(), double(), double())[NA]),
+ data.frame(x = NA_real_, y = NA_real_, z = NA_real_)
+ )
+})
+
+test_that("s2_point vectors can't have other types of objects concatenated or asssigned", {
+ point <- new_s2_xptr(list(NULL), class = "s2_point")
+ expect_is(c(point, point), "s2_point")
+ expect_error(c(point, new_s2_xptr(list(), class = "some_other_class")), "All items must inherit")
+ expect_error(point[1] <- new_s2_xptr(list(NULL), class = "some_other_class"), "no applicable method")
+ expect_error(point[[1]] <- new_s2_xptr(list(NULL), class = "some_other_class"), "no applicable method")
+})
+
+test_that("s2_point can be imported from s2_geography", {
+ expect_equal(
+ as.data.frame(as_s2_point(as_s2_geography("POINT (-64 45)"))),
+ as.data.frame(as_s2_point(as_s2_lnglat(as_s2_geography("POINT (-64 45)")))),
+ )
+})
+
+test_that("s2_point objects can be printed", {
+ expect_output(print(s2_point(1, 2, 3)), "s2_point")
+})
--- /dev/null
+
+test_that("s2_contains() works", {
+ expect_identical(s2_contains("POINT (0 0)", NA_character_), NA)
+
+ expect_true(s2_contains("POINT (0 0)", "POINT (0 0)"))
+ expect_false(s2_contains("POINT (0 0)", "POINT (1 1)"))
+ expect_false(s2_contains("POINT (0 0)", "POINT EMPTY"))
+
+ # make sure model is passed on to at least one binary predicate
+ # in the open model, lines do not contain endpoints (but do contain other points)
+ expect_false(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 0)", s2_options(model = "open")))
+ expect_true(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 1)", s2_options(model = "open")))
+ expect_false(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 0.5)", s2_options(model = "open")))
+
+ # semi-open and closed: endpoints are contained
+ expect_true(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 0)", s2_options(model = "semi-open")))
+ expect_true(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 1)", s2_options(model = "semi-open")))
+ expect_false(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 0.5)", s2_options(model = "semi-open")))
+
+ expect_true(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 0)", s2_options(model = "closed")))
+ expect_true(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 1)", s2_options(model = "closed")))
+ expect_false(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 0.5)", s2_options(model = "closed")))
+})
+
+test_that("s2_covers() and s2_covered_by() work", {
+ expect_true(s2_covers("POINT (0 0)", "POINT (0 0)"))
+ expect_false(s2_covers("POINT (0 0)", "POINT (1 1)"))
+ expect_false(s2_covers("POINT (0 0)", "POINT EMPTY"))
+ expect_true(s2_covers("LINESTRING (0 0, 1 1)", "POINT (0 0)"))
+ expect_false(s2_covers("LINESTRING (0 0, 1 1)", "POINT (-1 -1)"))
+
+ # make sure line is along a geodesic edge
+ # if the line doesn't contain the endpoints, floating
+ # point math won't always keep it strictly inside or outside
+ # the polygon (hard to predict which way it will go)
+ # (when the model is set such that the boundary of the polygon
+ # is considered 'contained' by it)
+ polygon <- "POLYGON ((0 0, 1 1, 0 1, 0 0))"
+ line <- "LINESTRING (0 0, 1 1)"
+ point <- "POINT (0.5 0.7)"
+
+ expect_true(s2_covers(polygon, polygon))
+ expect_false(s2_covers(polygon, line, s2_options(model = "open")))
+ expect_false(s2_covers(polygon, line, s2_options(model = "semi-open")))
+ expect_true(s2_covers(polygon, line, s2_options(model = "closed")))
+ expect_true(s2_covers(polygon, point, s2_options(model = "open")))
+ expect_true(s2_covers(polygon, point, s2_options(model = "semi-open")))
+ expect_true(s2_covers(polygon, point, s2_options(model = "closed")))
+ expect_false(s2_covered_by(polygon, line, s2_options(model = "open")))
+ expect_false(s2_covered_by(polygon, line, s2_options(model = "semi-open")))
+ expect_false(s2_covered_by(polygon, line, s2_options(model = "closed")))
+ expect_true(s2_covered_by(point, polygon, s2_options(model = "open")))
+ expect_true(s2_covered_by(point, polygon, s2_options(model = "semi-open")))
+ expect_true(s2_covered_by(point, polygon, s2_options(model = "closed")))
+})
+
+test_that("s2_disjoint() works", {
+ expect_identical(s2_disjoint("POINT (0 0)", NA_character_), NA)
+
+ expect_false(s2_disjoint("POINT (0 0)", "POINT (0 0)"))
+ expect_true(s2_disjoint("POINT (0 0)", "POINT (1 1)"))
+ expect_true(s2_disjoint("POINT (0 0)", "POINT EMPTY"))
+
+ expect_false(s2_disjoint("LINESTRING (0 0, 1 1)", "POINT (0 0)"))
+ expect_true(s2_disjoint("LINESTRING (0 0, 1 1)", "POINT (-1 -1)"))
+})
+
+test_that("s2_equals() works", {
+ expect_identical(s2_equals("POINT (0 0)", NA_character_), NA)
+
+ expect_true(s2_equals("POINT (0 0)", "POINT (0 0)"))
+ expect_true(s2_equals("POINT (0 0)", "POINT (0 0)", s2_options(model = "open")))
+ expect_true(s2_equals("POINT (0 0)", "POINT (0 0)", s2_options(model = "semi-open")))
+ expect_true(s2_equals("POINT (0 0)", "POINT (0 0)", s2_options(model = "closed")))
+ expect_false(s2_equals("POINT (0 0)", "POINT (1 1)"))
+ expect_false(s2_equals("POINT (0 0)", "POINT EMPTY"))
+ expect_true(s2_equals("POINT EMPTY", "POINT EMPTY"))
+
+ expect_true(s2_equals("LINESTRING (0 0, 1 1)", "LINESTRING (1 1, 0 0)"))
+ expect_false(s2_equals("LINESTRING (0 1, 1 1)", "LINESTRING (1 1, 0 0)"))
+})
+
+test_that("s2_intersects() works", {
+ expect_identical(s2_intersects("POINT (0 0)", NA_character_), NA)
+
+ expect_true(s2_intersects("POINT (0 0)", "POINT (0 0)"))
+ expect_false(s2_intersects("POINT (0 0)", "POINT (1 1)"))
+ expect_false(s2_intersects("POINT (0 0)", "POINT EMPTY"))
+
+ expect_true(s2_intersects("LINESTRING (0 0, 1 1)", "LINESTRING (1 0, 0 1)"))
+ expect_false(s2_intersects("LINESTRING (0 0, 1 1)", "LINESTRING (-2 -2, -1 -1)"))
+ expect_true(s2_intersects("LINESTRING (0 0, 1 1)", "POINT (0 0)"))
+ expect_false(s2_intersects("LINESTRING (0 0, 1 1)", "POINT (0 0)", s2_options(model = "open")))
+ expect_true(s2_intersects("LINESTRING (0 0, 1 1)", "POINT (0 0)", s2_options(model = "semi-open")))
+ expect_true(s2_intersects("LINESTRING (0 0, 1 1)", "POINT (0 0)", s2_options(model = "closed")))
+ polygon = "POLYGON((0 0,1 0,1 1,0 1,0 0))"
+ expect_false(s2_intersects(polygon, "POINT (0 0)"))
+ expect_false(s2_intersects(polygon, "POINT (0 0)", s2_options(model = "open")))
+ expect_false(s2_intersects(polygon, "POINT (0 0)", s2_options(model = "semi-open")))
+ expect_true(s2_intersects(polygon, "POINT (0 0)", s2_options(model = "closed")))
+})
+
+test_that("s2_intersects_box() works", {
+ expect_error(
+ s2_intersects_box("POINT (-1 -1)", -2, -2, 2, 2, detail = 0),
+ "Can't create polygon"
+ )
+ expect_true(s2_intersects_box("POINT (0.1 0.1)", 0, 0, 1, 1))
+ expect_true(s2_intersects_box("POINT (0.1 0.1)", 0, 0, 1, 1))
+ expect_true(s2_intersects_box("POINT (0.1 0.1)", 0, 0, 1, 1, options = s2_options(model = "open")))
+ expect_true(s2_intersects_box("POINT (0.1 0.1)", 0, 0, 1, 1, options = s2_options(model = "semi-open")))
+ expect_true(s2_intersects_box("POINT (0.1 0.1)", 0, 0, 1, 1, options = s2_options(model = "closed")))
+
+ expect_false(s2_intersects_box("POINT (1 1)", 0, 0, 1, 1))
+ expect_false(s2_intersects_box("POINT (0 0)", 0, 0, 1, 1))
+ expect_false(s2_intersects_box("POINT (0 0)", 0, 0, 1, 1))
+ expect_false(s2_intersects_box("POINT (0 0)", 0, 0, 1, 1, options = s2_options(model = "open")))
+ expect_false(s2_intersects_box("POINT (0 0)", 0, 0, 1, 1, options = s2_options(model = "semi-open")))
+ expect_true(s2_intersects_box("POINT (0 0)", 0, 0, 1, 1, options = s2_options(model = "closed")))
+
+ expect_true(s2_intersects_box("POINT (-1 -1)", -2, -2, 2, 2))
+ expect_false(s2_intersects_box("POINT (-1 -1)", 0, 0, 2, 2))
+ expect_false(s2_intersects_box("POINT (0 0)", 1, 1, 2, 2))
+})
+
+test_that("s2_within() works", {
+ expect_identical(s2_within("POINT (0 0)", NA_character_), NA)
+
+ expect_true(s2_within("POINT (0 0)", "POINT (0 0)"))
+ expect_false(s2_within("POINT (0 0)", "POINT (1 1)"))
+ expect_false(s2_within("POINT (0 0)", "POINT EMPTY"))
+ expect_false(s2_within("POINT EMPTY", "POINT (0 0)"))
+
+ expect_true(s2_within("POINT (0 0)", "LINESTRING (0 0, 1 1)", s2_options(model = "semi-open")))
+ expect_false(s2_within("POINT (0 0)", "LINESTRING (0 0, 1 1)", s2_options(model = "open")))
+ expect_false(s2_within("POINT (0 0)", "LINESTRING (1 1, 2 2)"))
+})
+
+test_that("s2_touches() works", {
+ # is inside
+ expect_false(s2_touches("POLYGON ((0 0, 0 1, 1 1, 0 0))", "POINT (0.5 0.75)"))
+ # is outside
+ expect_false(s2_touches("POLYGON ((0 0, 0 1, 1 1, 0 0))", "POINT (-0.5 0.75)"))
+
+ # is vertex
+ expect_true(s2_touches("POLYGON ((0 0, 0 1, 1 1, 0 0))", "POINT (0 0)"))
+})
+
+test_that("s2_dwithin() works", {
+ expect_identical(s2_dwithin("POINT (0 0)", NA_character_, 0), NA)
+
+ expect_true(s2_dwithin("POINT (0 0)", "POINT (90 0)", pi / 2 + 0.01, radius = 1))
+ expect_false(s2_dwithin("POINT (0 0)", "POINT (90 0)", pi / 2 - 0.01, radius = 1))
+ expect_false(s2_dwithin("POINT (0 0)", "POINT EMPTY", 0))
+
+ expect_true(s2_dwithin("LINESTRING (-45 0, 45 0)", "POINT (0 20)", 21, radius = 180 / pi))
+ expect_false(s2_dwithin("LINESTRING (-45 0, 45 0)", "POINT (0 20)", 19, radius = 180 / pi))
+
+ # check vectorization
+ expect_identical(
+ s2_dwithin("POINT (0 0)", "POINT (90 0)", pi / 2 + c(0.01, -0.01), radius = 1),
+ c(TRUE, FALSE)
+ )
+})
+
--- /dev/null
+
+test_that("s2_centroid() works", {
+ expect_wkt_equal(s2_centroid("POINT (30 10)"), "POINT (30 10)")
+ expect_true(s2_is_empty(s2_centroid("POINT EMPTY")))
+ expect_wkt_equal(s2_centroid("MULTIPOINT ((0 0), (0 10))"), "POINT (0 5)")
+ expect_wkt_equal(s2_centroid("LINESTRING (0 0, 0 10)"), "POINT (0 5)", precision = 15)
+ expect_near(
+ s2_distance(
+ s2_centroid("POLYGON ((-5 -5, 5 -5, 5 5, -5 5, -5 -5))"),
+ "POINT (0 0)"
+ ),
+ 0,
+ epsilon = 1e-6
+ )
+})
+
+test_that("s2_boundary() works", {
+ expect_true(s2_is_empty(s2_boundary("POINT (30 10)")))
+ expect_true(s2_is_empty(s2_boundary("POINT EMPTY")))
+ expect_true(s2_is_empty(s2_boundary("POLYGON EMPTY")))
+ expect_wkt_equal(s2_boundary("LINESTRING (0 0, 0 10)"), "MULTIPOINT ((0 0), (0 10))")
+
+ expect_wkt_equal(
+ s2_boundary("MULTIPOLYGON (
+ ((40 40, 20 45, 45 30, 40 40)),
+ ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20))
+ )"),
+ "MULTILINESTRING (
+ (40 40, 20 45, 45 30, 40 40),
+ (20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20)
+ )",
+ precision = 15
+ )
+})
+
+test_that("s2_closest_point() works", {
+ expect_wkt_equal(s2_closest_point("POINT (0 1)", "POINT (30 10)"), "POINT (0 1)")
+ expect_wkt_equal(s2_closest_point("LINESTRING (0 1, -12 -12)", "POINT (30 10)"), "POINT (0 1)")
+})
+
+test_that("s2_minimum_clearance_line_between() works", {
+ expect_wkt_equal(
+ s2_minimum_clearance_line_between("POINT (0 1)", "POINT (30 10)"),
+ "LINESTRING (0 1, 30 10)"
+ )
+ expect_true(s2_is_empty(s2_minimum_clearance_line_between("POINT (30 10)", "POINT EMPTY")))
+
+ expect_wkt_equal(
+ s2_minimum_clearance_line_between("LINESTRING (0 1, -12 -12)", "POINT (30 10)"),
+ "LINESTRING (0 1, 30 10)"
+ )
+ expect_wkt_equal(
+ s2_minimum_clearance_line_between("LINESTRING (0 0, 1 1)", "LINESTRING (1 0, 0 1)"),
+ "MULTIPOINT ((0.5 0.500057), (0.5 0.500057))",
+ precision = 6
+ )
+})
+
+test_that("s2_difference() works", {
+ expect_wkt_equal(s2_difference("POINT (30 10)", "POINT EMPTY"), "POINT (30 10)")
+ expect_true(s2_is_empty(s2_difference("POINT (30 10)", "POINT (30 10)")))
+
+ expect_true(s2_is_empty(s2_difference("LINESTRING (0 0, 45 0)", "LINESTRING (0 0, 45 0)")))
+})
+
+test_that("s2_difference() works for polygons", {
+ # on Windows i386, these fail without snap rounding
+ df <- s2_difference(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))",
+ s2_options(snap = s2_snap_level(30))
+ )
+
+ expect_near(
+ s2_area(df, radius = 1),
+ s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) -
+ s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1),
+ epsilon = 0.004
+ )
+
+ df0 <- s2_difference(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" ,
+ s2_options(model = "open", snap = s2_snap_level(30))
+ )
+ df1 <- s2_difference(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" ,
+ s2_options(model = "semi-open", snap = s2_snap_level(30))
+ )
+ df2 <- s2_difference(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" ,
+ s2_options(model = "closed", snap = s2_snap_level(30))
+ )
+ expect_equal(s2_area(df0) - s2_area(df2), 0.0)
+ expect_equal(s2_area(df0) - s2_area(df1), 0.0)
+})
+
+test_that("s2_sym_difference() works", {
+ expect_wkt_equal(s2_sym_difference("POINT (30 10)", "POINT EMPTY"), "POINT (30 10)")
+ expect_true(s2_is_empty(s2_sym_difference("POINT (30 10)", "POINT (30 10)")))
+ expect_wkt_equal(s2_sym_difference("POINT (30 10)", "POINT (30 20)"), "MULTIPOINT ((30 20), (30 10))")
+
+ expect_true(s2_is_empty(s2_sym_difference("LINESTRING (0 0, 45 0)", "LINESTRING (0 0, 45 0)")))
+})
+
+test_that("s2_sym_difference() works for polygons", {
+ # on Windows i386, these fail without snap rounding
+ df <- s2_sym_difference(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))",
+ s2_options(snap = s2_snap_level(30))
+ )
+
+ expect_near(
+ s2_area(df, radius = 1),
+ 2 * s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) -
+ s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1),
+ epsilon = 0.0042
+ )
+
+ df0 <- s2_sym_difference(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" ,
+ s2_options(model = "open", snap = s2_snap_level(30))
+ )
+
+ df1 <- s2_sym_difference(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" ,
+ s2_options(model = "semi-open", snap = s2_snap_level(30))
+ )
+
+ df2 = s2_sym_difference(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))",
+ s2_options(model = "closed", snap = s2_snap_level(30))
+ )
+ expect_equal(s2_area(df0) - s2_area(df2), 0.0)
+ expect_equal(s2_area(df0) - s2_area(df1), 0.0)
+})
+
+test_that("s2_intersection() works", {
+ expect_wkt_equal(s2_intersection("POINT (30 10)", "POINT (30 10)"), "POINT (30 10)")
+ expect_true(s2_is_empty(s2_intersection("POINT (30 10)", "POINT (30 11)")))
+
+ expect_wkt_equal(
+ s2_intersection(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "LINESTRING (0 5, 10 5)"
+ ),
+ "LINESTRING (0 5, 10 5)"
+ )
+
+ expect_equal(
+ s2_distance(
+ s2_intersection("LINESTRING (-45 0, 45 0)", "LINESTRING (0 -10, 0 10)"),
+ "POINT (0 0)"
+ ),
+ 0
+ )
+})
+
+test_that("s2_intersction() works for polygons", {
+ expect_wkt_equal(
+ s2_intersection(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))",
+ s2_options(snap = s2_snap_level(30))
+ ),
+ "POLYGON ((5 5, 10 5, 10 10, 5 10, 5 5))",
+ precision = 2
+ )
+ expect_true(s2_is_empty(s2_intersection("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POINT(0 0)")))
+ expect_true(
+ s2_is_empty(
+ s2_intersection(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POINT(0 0)", s2_options(model = "open")
+ )
+ )
+ )
+ expect_true(
+ s2_is_empty(
+ s2_intersection("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POINT(0 0)", s2_options(model = "semi-open"))
+ )
+ )
+ expect_wkt_equal(
+ s2_intersection("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POINT(0 0)", s2_options(model = "closed")),
+ "POINT(0 0)"
+ )
+
+ df0 <- s2_intersection(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" ,
+ s2_options(model = "open")
+ )
+ df1 <- s2_intersection(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" ,
+ s2_options(model = "semi-open")
+ )
+ df2 <- s2_intersection(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" ,
+ s2_options(model = "closed")
+ )
+
+ expect_equal(s2_area(df0) - s2_area(df2), 0.0)
+ expect_equal(s2_area(df0) - s2_area(df1), 0.0)
+})
+
+test_that("s2_union(x) works", {
+ expect_wkt_equal(s2_union("POINT (30 10)"), "POINT (30 10)")
+ expect_wkt_equal(s2_union("POINT EMPTY"), "GEOMETRYCOLLECTION EMPTY")
+ expect_wkt_equal(
+ s2_union("MULTILINESTRING ((-45 0, 0 0), (0 0, 0 10))"),
+ "LINESTRING (-45 0, 0 0, 0 10)"
+ )
+
+ expect_wkt_equal(s2_union("GEOMETRYCOLLECTION (POINT (30 10))"), "POINT (30 10)")
+ expect_wkt_equal(
+ s2_union("GEOMETRYCOLLECTION (POINT (30 10), LINESTRING (0 0, 0 1))"),
+ "GEOMETRYCOLLECTION (POINT (30 10), LINESTRING (0 0, 0 1))"
+ )
+})
+
+test_that("s2_union(x, y) works", {
+ expect_wkt_equal(s2_union("POINT (30 10)", "POINT EMPTY"), "POINT (30 10)")
+ expect_wkt_equal(s2_union("POINT EMPTY", "POINT EMPTY"), "GEOMETRYCOLLECTION EMPTY")
+
+ # LINESTRING (-45 0, 0 0, 0 10)
+ expect_wkt_equal(
+ s2_union("LINESTRING (-45 0, 0 0)", "LINESTRING (0 0, 0 10)"),
+ "LINESTRING (-45 0, 0 0, 0 10)"
+ )
+})
+
+test_that("s2_union() works for polygons", {
+ # on Windows i386, these fail without snap rounding
+ u <- s2_union(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))",
+ s2_options(snap = s2_snap_level(30))
+ )
+ u0 <- s2_union(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" ,
+ s2_options(model = "open", snap = s2_snap_level(30))
+ )
+ u1 <- s2_union(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" ,
+ s2_options(model = "semi-open", snap = s2_snap_level(30))
+ )
+ u2 <- s2_union(
+ "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+ "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" ,
+ s2_options(model = "closed", snap = s2_snap_level(30))
+ )
+ expect_equal(s2_area(u0) - s2_area(u2), 0.0)
+ expect_equal(s2_area(u0) - s2_area(u1), 0.0)
+
+
+ expect_near(
+ s2_area(u, radius = 1),
+ s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) +
+ s2_area("POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", radius = 1) -
+ s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1),
+ epsilon = 0.004
+ )
+})
+
+test_that("binary operations use layer creation options", {
+ expect_wkt_equal(
+ s2_union(
+ "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)",
+ options = s2_options(polyline_type = "path", polyline_sibling_pairs = "discard")
+ ),
+ "LINESTRING (0 0, 0 1, 0 2, 0 3)"
+ )
+ expect_true(
+ s2_is_collection(
+ s2_union(
+ "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)",
+ options = s2_options(polyline_type = "walk")
+ )
+ )
+ )
+
+ expect_wkt_equal(
+ s2_union_agg(
+ "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)",
+ options = s2_options(polyline_type = "path", polyline_sibling_pairs = "discard")
+ ),
+ "LINESTRING (0 0, 0 1, 0 2, 0 3)"
+ )
+ expect_true(
+ s2_is_collection(
+ s2_union_agg(
+ "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)",
+ options = s2_options(polyline_type = "walk")
+ )
+ )
+ )
+})
+
+test_that("s2_union_agg() works", {
+ expect_wkt_equal(s2_union_agg(c("POINT (30 10)", "POINT EMPTY")), "POINT (30 10)")
+ expect_wkt_equal(s2_union_agg(c("POINT EMPTY", "POINT EMPTY")), "GEOMETRYCOLLECTION EMPTY")
+
+ # NULL handling
+ expect_identical(
+ s2_union_agg(c("POINT (30 10)", NA), na.rm = FALSE),
+ as_s2_geography(NA_character_)
+ )
+ expect_wkt_equal(
+ s2_union_agg(c("POINT (30 10)", NA), na.rm = TRUE),
+ "POINT (30 10)"
+ )
+})
+
+test_that("s2_centroid_agg() works", {
+ expect_wkt_equal(s2_centroid_agg(c("POINT (30 10)", "POINT EMPTY")), "POINT (30 10)")
+ expect_wkt_equal(s2_centroid_agg(c("POINT EMPTY", "POINT EMPTY")), "POINT EMPTY")
+ expect_wkt_equal(s2_centroid_agg(c("POINT (0 0)", "POINT (0 10)")), "POINT (0 5)", precision = 15)
+
+ # NULL handling
+ expect_identical(
+ s2_centroid_agg(c("POINT (30 10)", NA), na.rm = FALSE),
+ as_s2_geography(NA_character_)
+ )
+ expect_wkt_equal(
+ s2_centroid_agg(c("POINT (30 10)", NA), na.rm = TRUE),
+ "POINT (30 10)"
+ )
+})
+
+test_that("s2_snap_to_grid() works", {
+ expect_wkt_equal(
+ s2_as_text(s2_snap_to_grid("POINT (0.333333333333 0.666666666666)", 1e-2)),
+ "POINT (0.33 0.67)",
+ precision = 6
+ )
+})
+
+test_that("s2_buffer() works", {
+ # create a hemisphere!
+ ply <- s2_buffer_cells("POINT (0 0)", distance = pi / 2, radius = 1)
+ expect_near(s2_area(ply, radius = 1), 4 * pi / 2, epsilon = 0.1)
+})
+
+test_that("s2_simplify() works", {
+ expect_wkt_equal(
+ s2_simplify("LINESTRING (0 0, 0.001 1, -0.001 2, 0 3)", tolerance = 100),
+ "LINESTRING (0 0, 0.001 1, -0.001 2, 0 3)"
+ )
+ expect_wkt_equal(
+ s2_simplify("LINESTRING (0 0, 0.001 1, -0.001 2, 0 3)", tolerance = 1000),
+ "LINESTRING (0 0, 0 3)"
+ )
+})
+
+test_that("s2_rebuild() works", {
+ s2_rebuild("POINT (-64 45)")
+
+ s2_rebuild("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))")
+ s2_rebuild("GEOMETRYCOLLECTION (POINT (-64 45), LINESTRING (-64 45, 0 0))")
+
+ # duplicated edges
+ expect_wkt_equal(
+ s2_rebuild("MULTIPOINT (-64 45, -64 45)", options = s2_options(duplicate_edges = FALSE)),
+ "POINT (-64 45)"
+ )
+ expect_wkt_equal(
+ s2_rebuild("MULTIPOINT (-64 45, -64 45)", options = s2_options(duplicate_edges = TRUE)),
+ "MULTIPOINT (-64 45, -64 45)"
+ )
+
+ # crossing edges
+ expect_true(
+ s2_is_collection(
+ s2_rebuild(
+ "LINESTRING (0 -5, 0 5, -5 0, 5 0)",
+ options = s2_options(split_crossing_edges = TRUE)
+ )
+ )
+ )
+
+ # snap
+ expect_wkt_equal(
+ s2_rebuild(
+ "MULTIPOINT (0.01 0.01, -0.01 -0.01)",
+ options = s2_options(
+ snap = s2_snap_precision(1e1),
+ duplicate_edges = TRUE
+ )
+ ),
+ "MULTIPOINT ((0 0), (0 0))"
+ )
+
+ # snap radius
+ expect_wkt_equal(
+ s2_rebuild(
+ "LINESTRING (0 0, 0 1, 0 2, 0 3)",
+ options = s2_options(
+ snap_radius = 1.5 * pi / 180
+ )
+ ),
+ "LINESTRING (0 0, 0 2)"
+ )
+
+ # simplify edge chains
+ expect_wkt_equal(
+ s2_rebuild(
+ "LINESTRING (0 0, 0 1, 0 2, 0 3)",
+ options = s2_options(
+ snap_radius = 0.01,
+ simplify_edge_chains = TRUE
+ )
+ ),
+ "LINESTRING (0 0, 0 3)"
+ )
+
+ # validate
+ bad_poly <- s2_geog_from_text(
+ "POLYGON ((0 0, 1.0 0, 1.0 1.0, -0.1 1.0, 1.1 0, 0 0))",
+ check = FALSE
+ )
+ expect_wkt_equal(
+ s2_rebuild(bad_poly, options = s2_options(validate = FALSE)),
+ bad_poly
+ )
+ expect_error(
+ s2_rebuild(bad_poly, options = s2_options(validate = TRUE)),
+ "Edge 1 crosses edge 3"
+ )
+
+ # polyline type
+ expect_wkt_equal(
+ s2_rebuild(
+ "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)",
+ s2_options(polyline_type = "walk")
+ ),
+ "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)"
+ )
+ expect_true(
+ s2_is_collection(
+ s2_rebuild(
+ "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)",
+ s2_options(polyline_type = "path")
+ )
+ )
+ )
+
+ # sibling edge pairs
+ expect_true(
+ s2_is_collection(
+ s2_rebuild(
+ "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)",
+ s2_options(polyline_type = "path", polyline_sibling_pairs = "keep")
+ )
+ )
+ )
+ expect_false(
+ s2_is_collection(
+ s2_rebuild(
+ "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)",
+ s2_options(polyline_type = "path", polyline_sibling_pairs = "discard")
+ )
+ )
+ )
+})
+
+test_that("real data survives the S2BooleanOperation", {
+ # the 32-bit Solaris build results in some of the roundtripped
+ # edges becoming degenerate. Rather than pass check = FALSE to
+ # as_s2_geography(), just skip this on Solaris
+ skip_on_os("solaris")
+
+ for (continent in unique(s2::s2_data_tbl_countries$continent)) {
+ # this is primarily a test of the S2BooleanOperation -> Geography constructor
+ unioned <- expect_is(s2_union_agg(s2_data_countries(continent)), "s2_geography")
+
+ # this is a test of Geography::Export() on potentially complex polygons
+ exported <- expect_length(s2_as_binary(unioned), 1)
+
+ # the output WKB should load as a polygon with oriented = TRUE and result in the
+ # same number of points and similar area
+ reloaded <- as_s2_geography(structure(exported, class = "wk_wkb"), oriented = TRUE)
+ expect_equal(s2_num_points(reloaded), s2_num_points(unioned))
+ expect_equal(s2_area(reloaded, radius = 1), s2_area(unioned, radius = 1))
+
+ # also check with oriented = FALSE (may catch quirky nesting)
+ reloaded <- as_s2_geography(structure(exported, class = "wk_wkb"), oriented = FALSE)
+ expect_equal(s2_num_points(reloaded), s2_num_points(unioned))
+ expect_equal(s2_area(reloaded, radius = 1), s2_area(unioned, radius = 1))
+ }
+})
--- /dev/null
+
+test_that("s2_xptr class works", {
+ expect_s3_class(new_s2_xptr(list()), "s2_xptr")
+ expect_s3_class(new_s2_xptr(list(), class = "custom"), "custom")
+ expect_s3_class(new_s2_xptr(list(), class = "custom"), "s2_xptr")
+ expect_error(new_s2_xptr(NULL), "must be a bare list")
+})
+
+test_that("objects pointed to by an s2_xptr are destroyed by the garbage collector", {
+ xptr <- expect_output(s2_xptr_test(1), "Allocating")
+ expect_output(s2_xptr_test_op(xptr), "test\\(\\) on XPtrTest")
+ expect_identical(validate_s2_xptr(xptr), xptr)
+ expect_output({rm(xptr); gc()}, "Destroying")
+})
+
+test_that("s2_xptr validation works", {
+ expect_identical(validate_s2_xptr(new_s2_xptr(list())), new_s2_xptr(list()))
+ expect_identical(validate_s2_xptr(new_s2_xptr(list(NULL))), new_s2_xptr(list(NULL)))
+ expect_error(validate_s2_xptr(list("wrong type")), "must be externalptr")
+})
+
+test_that("s2_xptr subsetting and concatenation work", {
+ expect_length(rep(new_s2_xptr(list()), 10), 0)
+
+ xptr <- new_s2_xptr(list(NULL, NULL))
+ expect_identical(xptr[1], new_s2_xptr(list(NULL)))
+ expect_identical(xptr[[1]], xptr[1])
+ expect_identical(c(xptr, xptr), new_s2_xptr(list(NULL, NULL, NULL, NULL)))
+ expect_identical(rep(xptr, 2), new_s2_xptr(list(NULL, NULL, NULL, NULL)))
+ expect_identical(rep_len(xptr, 4), new_s2_xptr(list(NULL, NULL, NULL, NULL)))
+})
+
+test_that("s2_xptr default print method works", {
+ expect_output(print(new_s2_xptr()), "s2_xptr")
+ expect_output(print(new_s2_xptr(list(NULL))), "s2_xptr")
+})
--- /dev/null
+
+test_that("recycle_common works", {
+ expect_identical(recycle_common(1, 2), list(1, 2))
+ expect_identical(recycle_common(1, b = 2), list(1, b = 2))
+ expect_identical(recycle_common(1, 2:4), list(c(1, 1, 1), c(2L, 3L, 4L)))
+ expect_identical(recycle_common(numeric(0), 2), list(numeric(0), numeric(0)))
+ expect_error(recycle_common(numeric(0), 2:4), "Incompatible lengths")
+})
+
+test_that("wkt tester works", {
+ expect_wkt_equal("POINT (0.123456 0)", "POINT (0.1234561 0)", precision = 6)
+ expect_failure(expect_wkt_equal("POINT (0.123456 0)", "POINT (0.1234561 0)", precision = 7))
+})
+
+test_that("almost equal expectation works", {
+ expect_near(0.001, 0, epsilon = 0.0011)
+ expect_failure(expect_near(0.001, 0, epsilon = 0.0009))
+})
--- /dev/null
+
+test_that("s2_geography is a vctr", {
+ x <- new_s2_xptr(list(NULL), "s2_geography")
+ expect_true(vctrs::vec_is(x))
+ expect_identical(vctrs::vec_data(x), list(NULL))
+ expect_identical(vctrs::vec_restore(list(NULL), x), x)
+ expect_identical(vctrs::vec_ptype_abbr(x), class(x)[1])
+})
+
+test_that("s2_lng latis a vctr", {
+ x <- new_s2_xptr(list(NULL), "s2_lnglat")
+ expect_true(vctrs::vec_is(x))
+ expect_identical(vctrs::vec_data(x), list(NULL))
+ expect_identical(vctrs::vec_restore(list(NULL), x), x)
+ expect_identical(vctrs::vec_ptype_abbr(x), class(x)[1])
+})
+
+test_that("s2_point is a vctr", {
+ x <- new_s2_xptr(list(NULL), "s2_point")
+ expect_true(vctrs::vec_is(x))
+ expect_identical(vctrs::vec_data(x), list(NULL))
+ expect_identical(vctrs::vec_restore(list(NULL), x), x)
+ expect_identical(vctrs::vec_ptype_abbr(x), class(x)[1])
+})
--- /dev/null
+#include <openssl/opensslv.h>
+#if OPENSSL_VERSION_NUMBER < 0x10000000L
+#error OpenSSL version too old
+#endif
--- /dev/null
+# Build against openssl libraries that were compiled with the Rtools gcc toolchain.
+if(!file.exists("../windows/openssl-1.1.1/include/openssl/ssl.h")){
+ if(getRversion() < "3.3.0") setInternet2()
+ download.file("https://github.com/rwinlib/openssl/archive/v1.1.1.zip", "lib.zip", quiet = TRUE)
+ dir.create("../windows", showWarnings = FALSE)
+ unzip("lib.zip", exdir = "../windows")
+ unlink("lib.zip")
+}